/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* |_o_o|\\ Copyright (c) 1986 The Software Distillery.  All Rights Reserved */
/* |. o.| || This program may not be distributed without the permission of   */
/* | .  | || the authors.                                                    */
/* | o  | ||    Dave Baker     Ed Burnette  Stan Chow    Jay Denebeim        */
/* |  . |//     Gordon Keener  Jack Rouse   John Toebes  Doug Walker         */
/* ======          BBS:(919)-471-6436      VOICE:(919)-469-4210              */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*
 * VERY loosely based on the input.device example by Rob Peck, 12/1/85
 *
 *
 * Additions and modifications Copyright (c) 1989, 1990, 1991 Loren J. Rittle
 * Modifications Copyright (c) 1990 Ed A. Hutchins and Loren J. Rittle
 *
 * As stated in the original POPCLI documentation file:
 *
 * Permission is hereby granted to distribute this program provided
 * both the documentation accompanies the executable and that no
 * charge is made for its distribution.
 * I assume that this statement includes the right to modify the source
 * and futher distribute the modified work under this same agreement. - LJR
 * (Actually, at this point, 'diff newpop.c popcli3.c | wc'
 * > 'wc -c newpop.c' + 'wc -c popcli3.c'. :-)
 *
 *
 *	Note:	Some code to do the Q*X pattern lifted (and modified) from:
 * 		Line drawing demo for the Commodore/Amiga
 * 		Written by John Riley, Lattice, Inc.
 *
 *	Note:   The spawning of a lower priority task to do graphics
 *		was inspired by Mackie to some degree (and by
 *		one person who claimed that NewPop slowed down
 *		HST modem transfers! I think that they were
 *		on a non-A3000 class machine :-)  Later, I got
 *		a report that it also happened on an A3000! :-(
 *		See NICENEWPOP in `[s:].newpoprc'.
 *
 *	UpfrontLayer is used instead of WindowToFront() because of
 *	a WorkBench/INTUITION bug. From dmouse.doc:
 *
 *	WORKBENCH USERS!!!!!!!!!! There appears to be a bug in
 *	intuition's WindowToFront() call, which can lock up
 * 	intuition when workbench icons are active.  Under the
 *	defaults, this will occur whenever you depress the left
 * 	mouse button over an icon.
 *
 * 	Thanks to Matt Dillon for showing the work around
 * 	in DMouse's source code.
 *
 *	LJR added the titlebar clock sometime in 1988 or early 1989.
 *	LJR added the Q*X pattern sometime in 1988 or early 1989.
 *	LJR added the auto- and defeat- blanking in 1988 or early 1989.
 *	EAH and LJR added color to the Q*X sometime in late 1989.
 *	EAH and LJR added the titlebar PrefMon around 1/18/90.
 *	LJR improved the titlebar PrefMon on 2/8/90.
 *	LJR added the titlebar disk I/O PrefMon on 2/8/90.
 *	LJR reworked the way the titlebar stuff renders into
 *	    the `titlebar', now implemented with a backdrop window
 *	    positioned over the titlebar on 12/20/90.
 *	LJR reworked all of newpop to be a bit more system independent
 *	    on 12/30/90.
 *	LJR reworked Q*X code to spawn a new task on 1/13/91.
 *	LJR reworked parts of newpop to be a bit more explicit
 *	    in declaring which registers arguments *MUST* be passed
 *	    in (mainly for the event handler).  Also switched back
 *	    to '-b1' and we now use __saveds on functions that need
 *	    to load A4.  This saves a few hundred bytes. 1/18/91.
 *	LJR got rid of all _enforcer_ hits NewPop was causing.
 *	    The strange thing is that they were all happening in
 *	    ROM code because my message ports had no names and had
 *	    not been added to the system's port list.  I now name
 *	    and add to the system's list all message ports.  Not
 *	    only was this a bitch to track down, but it should
 *	    also be legal!  If anyone can comment on this, send me
 *	    mail at `l-rittle@uiuc.edu'. 1/19/91.
 *	LJR recoded the entire thing to allow dynamic (at startup
 *	    time) configuration instead of hardcoded compile time
 *	    configuration.  This added many, many bytes! Why did I
 *	    do it?  Because my public asked for it, will I ever get
 *	    a pizza out of this deal? YOW 1/27/91.
 *	LJR added URC, LRC, LLC, ULC options to allow the user
 *	    to decide what NewPop should do when the pointer enters
 *	    the given screen corner.  Also, changed the method
 *	    by which a running NewPop is told to halt. 2/1/91
 *	LJR modified the method used to shut down a running
 *	    NewPop:  Signal(NewPopTask, SIGBREAKF_CTRL_C) is now the
 *	    way to kill it, instead of a custom message with the quit
 *	    flag set. 2/1/91
 *	LJR cleaned up one last time for first UseNet release. 3/16/91
 */


/* * * * * * * * * * * INCLUDES * * * * * * * * * * * * */
#include <exec/types.h>
#include <exec/exec.h>
#include <exec/execbase.h>
#include <exec/tasks.h>
#include <devices/timer.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <libraries/dos.h>
#include <graphics/gfxmacros.h>
#include <graphics/layers.h>
#include <graphics/gfxbase.h>
#include <hardware/custom.h>
#include <hardware/dmabits.h>
#include <graphics/text.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/layers.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/diskfont.h>
#include <string.h>
#include <time.h>


/* * * * * * * * * * * CONSTANTS * * * * * * * * * * * * */
#define PORTNAME 	"NewPop.port"
 /* The name of our well known *PRIVATE* message port */

#define MAXCMD		200
#define MAXTASKNAME	32
#define MAXFONTNAME	32
 /* Maximum string size of various things */

#define KILLMSG		"\x9B" "0;33mNewPop\x9B" "0m Terminating\n"
 /* The kill message */

#define BANNER		"\x9B" "0;33mNewPop\x9B" "0m v3.2 by Loren J. Rittle - Copyright \xA9 1989 - 1991 Based upon:\n" \
			"\x9B" "0;33mPOPCLI III\x9B" "0m by John Toebes - Copyright \xA9 1987 The Software Distillery\n"
 /* The hello banner */


/* * * * * * * * * * * * * * * * TYPES * * * * * * * * * * * * * */
struct GLOBAL_DATA
{
  struct Task *buddy;
  ULONG creatclisig;
  ULONG unblanksig;
  ULONG mousemovesig;
  ULONG noevents;
  short creatsignum;
  short blanksignum;
  short mousemovenum;
  short key;
  struct Screen *blankscreen;
};

struct color
{
  int red, green, blue;
};

enum OptionTypes
{
  boolean, integer, string
};

struct NewPopOption
{
  char *name;
  enum OptionTypes type;
  union
  {
    int ivalue;
    char *svalue;
  } value;
  int min, max;
};


/* * * * * * * * * * * EXTERNALS AND GLOBAL DATA * * * * * * * * */
/*
 *  See .newpoprc for the meanings of these options!
 */
struct NewPopOption NewPopOptions[] =
{
#define NICENEWPOP (NewPopOptions[0].value.ivalue)
  {"NICENEWPOP", boolean, 1, 0, 1},
#define NEWPOPLITE (NewPopOptions[1].value.ivalue)
  {"NEWPOPLITE", boolean, 0, 0, 1},
#define WEWANTWINDOWDRAG (NewPopOptions[2].value.ivalue)
  {"WEWANTWINDOWDRAG", boolean, 0, 0, 1},
#define SCSITASKNAME (NewPopOptions[3].value.svalue)
  {"SCSITASKNAME", string, (int) "gvpscsi.device", 0, MAXTASKNAME},
#define FONTNAME (NewPopOptions[4].value.svalue)
  {"FONTNAME", string, (int) "Helvetica.font", 0, MAXFONTNAME},
#define COMMAND (NewPopOptions[5].value.svalue)
#ifndef LJR_FAVORITE
  {"COMMAND", string, (int) "NEWCLI >NIL:", 0, MAXCMD},
#else
  {"COMMAND", string, (int) "NewWSH <nil: >nil: cnn:0/11/640/189/WShell/c", 0, MAXCMD},
#endif
#define MAXSCSIUSAGE (NewPopOptions[6].value.ivalue)
  {"MAXSCSIUSAGE", integer, 55, 20, 255},
#define SCSIIOMONWIDTH (NewPopOptions[7].value.ivalue)
  {"SCSIIOMONWIDTH", integer, 100, 0, 100},
#define CPULOADMONWIDTH (NewPopOptions[8].value.ivalue)
  {"CPULOADMONWIDTH", integer, 100, 0, 100},
#define GAPBETWEEN2 (NewPopOptions[9].value.ivalue)
  {"GAPBETWEEN2", integer, 16, 0, 32},
#define GAPBETWEEN (NewPopOptions[10].value.ivalue)
  {"GAPBETWEEN", integer, 16, 0, 32},
#define FONTSIZE (NewPopOptions[11].value.ivalue)
  {"FONTSIZE", integer, 9, 4, 56},
#define STARTYOFFSET (NewPopOptions[12].value.ivalue)
  {"STARTYOFFSET", integer, 7, 2, 60},
#define MAXTEXTWIDTH (NewPopOptions[13].value.ivalue)
  {"MAXTEXTWIDTH", integer, 107, 10, 300},
#define STARTXOFFSET (NewPopOptions[14].value.ivalue)
  {"STARTXOFFSET", integer, 56, 10, 200},
#define WINDOWHEIGHT (NewPopOptions[15].value.ivalue)
  {"WINDOWHEIGHT", integer, 8, 4, 56},
#define TIMEOUT (NewPopOptions[16].value.ivalue)
  {"TIMEOUT", integer, 180, 0, 0x00ffffff},
#define POPKEY (NewPopOptions[17].value.ivalue)
  {"POPKEY", integer, 69, 0, 255},
#define BLANKERPRIORITY (NewPopOptions[18].value.ivalue)
  {"BLANKERPRIORITY", integer, -20, -20, 10},
#define ULC (NewPopOptions[19].value.ivalue)
  {"ULC", integer, 0, 0, 2},
#define URC (NewPopOptions[20].value.ivalue)
  {"URC", integer, 1, 0, 2},
#define LLC (NewPopOptions[21].value.ivalue)
  {"LLC", integer, 0, 0, 2},
#define LRC (NewPopOptions[22].value.ivalue)
  {"LRC", integer, 2, 0, 2},
/* Add new enties before this line! */
  {NULL, boolean, 0, 0, 0}
};

char NewFontName[MAXFONTNAME];
char NewTaskName[MAXTASKNAME];
char NewCmd[MAXCMD];

struct IntuitionBase *IntuitionBase;
struct LayersBase *LayersBase;
struct GfxBase *GfxBase;
struct DiskfontBase *DiskfontBase;
struct DosLibrary *DosBase;
struct ExecBase *ExecBase;

struct NewScreen NewScreen =
{0, 0, 640, 400, 1, 0, 1, HIRES | LACE, SCREENQUIET | CUSTOMSCREEN, NULL, NULL, NULL, NULL};

int WindowWidth, WindowHeight;

struct Gadget WDragGadget =
{NULL, 0, 0, 0, 0, GADGHNONE, NULL, WDRAGGING, NULL, NULL, NULL, NULL, NULL, 0, 0};

struct NewWindow NewWindow =
{
  0, 0,				/* window XY origin relative to TopLeft of screen.  Set later. */
  0, 0,				/* window width and height */
  2, 1,				/* detail and block pens */
  0,				/* IDCMP flags */
  BACKDROP + BORDERLESS + NOCAREREFRESH,	/* other window flags */
  &WDragGadget,			/* first gadget in gadget list */
  NULL,				/* custom CHECKMARK imagery */
  NULL,				/* window title */
  NULL,				/* custom screen pointer */
  NULL,				/* custom bitmap */
  0, 0,				/* minimum width and height */
  0, 0,				/* maximum width and height */
  WBENCHSCREEN			/* destination screen type */
};

extern BPTR _Backstdout;	/* standard output when run in background */
long _BackGroundIO = 1;		/* Flag to tell it we want to do I/O      */
long _stack = 4000;		/* Amount of stack space our task needs   */
char *_procname = "NewPop";	/* The name of the task to create         */
long _priority = 20;		/* The priority to run us at              */

struct GLOBAL_DATA global;
ULONG scsiDisp = 0;
struct Task *child = NULL;
struct ViewPort *blankVP = NULL;
struct RastPort *blankRP = NULL;
SHORT x[2], y[2], xd[2], yd[2], ox[2][16], oy[2][16];
USHORT xlim, ylim, j;

/* * * * * * * * * * * PROTOTYPES * * * * * * * * * * * * */
struct InputEvent *__saveds __asm myhandler (register __a0 struct InputEvent *);
void createchild (void);
void __saveds blankertask (void);
void killchild (void);
void __saveds scsifriendlosing (void);
int ParseConfigFile (void);
void _main (char *);
void MemCleanup (void);
int sprintf (char *, char *,...);
int abs (int);
long rand (void);
void changecolor (void);
void updatedisplay (void);
void QueueTimer (struct timerequest *, ULONG);
struct MsgPort *CreatePort (char *, long);
void DeletePort (struct MsgPort *);
struct IOStdReq *CreateIOReq (struct MsgPort *, int);
void DeleteIOReq (struct IOStdReq *);


/* * * * * * * * * * * OUR ROUTINES * * * * * * * * * * * */
struct InputEvent *__saveds __asm 
myhandler (register __a0 struct InputEvent * ev)
{
  struct InputEvent *ep, *laste;

  for (ep = ev, laste = NULL; ep != NULL; ep = ep->ie_NextEvent)
    {
      if ((ep->ie_Class == IECLASS_RAWKEY) &&
	  (ep->ie_Code == global.key) &&
	  (ep->ie_Qualifier & IEQUALIFIER_LCOMMAND) &&
	  (global.key))
	{
	  if (laste == NULL)
	    ev = ep->ie_NextEvent;
	  else
	    laste->ie_NextEvent = ep->ie_NextEvent;
	  Signal (global.buddy, global.creatclisig);
	}
      else
	laste = ep;

      if (ep->ie_Class == IECLASS_RAWMOUSE)
	Signal (global.buddy, global.mousemovesig);

      if (ep->ie_Class != IECLASS_TIMER)
	{
	  global.noevents = 0;
	  if (global.blankscreen)
	    Signal (global.buddy, global.unblanksig);
	}
    }
  return (ev);
}

void 
createchild (void)
{
  child = CreateTask ("NewPopBlanker", BLANKERPRIORITY, (APTR) blankertask, 4000L);
}

void __saveds 
blankertask (void)
{
  struct MsgPort *timerport2;
  struct timerequest *timerreq2 = NULL;

  if (timerport2 = CreatePort ("NewPopTimerPort2", 0))
    if (timerreq2 = (struct timerequest *) CreateIOReq (timerport2, sizeof (struct timerequest)))
      if (!(OpenDevice (TIMERNAME, UNIT_VBLANK, (struct IORequest *) timerreq2, 0)))
	{
	  while (!(SetSignal (0L, 0L) & SIGBREAKF_CTRL_C))
	    {
	      updatedisplay ();
	      if (child->tc_Node.ln_Pri == 10)
		SetTaskPri (child, BLANKERPRIORITY);
	      timerreq2->tr_node.io_Command = TR_ADDREQUEST;
	      timerreq2->tr_time.tv_secs = 0;
	      timerreq2->tr_time.tv_micro = 1000000 / 60;
	      DoIO ((struct IORequest *) timerreq2);
	    }
	}
      else
	Wait (SIGBREAKF_CTRL_C);
  if (timerreq2)
    {
      if (timerreq2->tr_node.io_Device)
	CloseDevice ((struct IORequest *) timerreq2);
      DeleteIOReq ((struct IOStdReq *) timerreq2);
    }
  if (timerport2)
    DeletePort (timerport2);
  Signal (global.buddy, SIGBREAKF_CTRL_D);
  Wait (0L);
}

void 
killchild (void)
{
  Signal (child, SIGBREAKF_CTRL_C);
  SetTaskPri (child, 20L);
  Wait (SIGBREAKF_CTRL_D);
  DeleteTask (child);
  child = NULL;
}

void __saveds 
scsifriendlosing (void)
{
  scsiDisp++;
}

int 
ParseConfigFile (void)
{
  BPTR configFile;
#define INPUTBUFFERSIZE 512
  char buffer[INPUTBUFFERSIZE];
  int bp;
  int keyword;
#define KEYWORDBUFFERSIZE 31
  char keywordbuffer[KEYWORDBUFFERSIZE + 1];
  int keywordbp;
  int charinbuffer;
  int state = 0;
  int line = 1;
  int i;
  int neg;

  configFile = Open (".newpoprc", MODE_OLDFILE);
  if (!configFile)
    configFile = Open ("s:.newpoprc", MODE_OLDFILE);
  if (!configFile)
    return 0;

  while (charinbuffer = Read (configFile, buffer, INPUTBUFFERSIZE))
    {
      for (bp = 0; bp != charinbuffer; bp++)
	{
	  switch (state)
	    {
	    case 0:
	      if (buffer[bp] == '#')
		state = 1;
	      else if (buffer[bp] == '\n')
		line++;
	      else if ((buffer[bp] <= 'z') && (buffer[bp] >= 'a'))
		{
		  keywordbuffer[0] = buffer[bp] - 32;
		  keywordbp = 1;
		  state = 2;
		}
	      else if ((buffer[bp] <= 'Z') && (buffer[bp] >= 'A'))
		{
		  keywordbuffer[0] = buffer[bp];
		  keywordbp = 1;
		  state = 2;
		}
	      else if ((buffer[bp] != ' ') && (buffer[bp] != '\t'))
		{
		  state = 1;
		  goto out;
		}
	      break;
	    case 1:
	      if (buffer[bp] == '\n')
		{
		  line++;
		  state = 0;
		}
	      break;
	    case 2:
	      if ((buffer[bp] <= 'z') && (buffer[bp] >= 'a'))
		{
		  if (keywordbp >= KEYWORDBUFFERSIZE)
		    {
		      state = 1;
		      goto out;
		    }
		  keywordbuffer[keywordbp++] = buffer[bp] - 32;
		}
	      else if ((buffer[bp] <= 'Z') && (buffer[bp] >= 'A'))
		{
		  if (keywordbp >= KEYWORDBUFFERSIZE)
		    {
		      state = 1;
		      goto out;
		    }
		  keywordbuffer[keywordbp++] = buffer[bp];
		}
	      else if ((buffer[bp] <= '9') && (buffer[bp] >= '0'))
		{
		  if (keywordbp >= KEYWORDBUFFERSIZE)
		    {
		      state = 1;
		      goto out;
		    }
		  keywordbuffer[keywordbp++] = buffer[bp];
		}
	      else if ((buffer[bp] == ' ') || (buffer[bp] == '\t'))
		{
		  keywordbuffer[keywordbp] = '\0';
		  for (i = 0; NewPopOptions[i].name; i++)
		    if (!strcmp (NewPopOptions[i].name, keywordbuffer))
		      break;
		  if (!NewPopOptions[i].name)
		    {
		      state = 1;
		      goto out;
		    }
		  keyword = i;
		  state = 3;
		}
	      else
		{
		  state = 1;
		  goto out;
		}
	      break;
	    case 3:
	      if ((buffer[bp] != ' ') && (buffer[bp] != '\t'))
		switch (NewPopOptions[keyword].type)
		  {
		  case boolean:
		    state = 4;
		    if ((buffer[bp] <= 'z') && (buffer[bp] >= 'a'))
		      {
			keywordbuffer[0] = buffer[bp] - 32;
			keywordbp = 1;
		      }
		    else if ((buffer[bp] <= 'Z') && (buffer[bp] >= 'A'))
		      {
			keywordbuffer[0] = buffer[bp];
			keywordbp = 1;
		      }
		    else
		      {
			state = 1;
			goto out;
		      }
		    break;
		  case integer:
		    state = 5;
		    neg = 0;
		    if (buffer[bp] == '-')
		      {
			neg = 1;
			NewPopOptions[keyword].value.ivalue = 0;
		      }
		    else if ((buffer[bp] <= '9') && (buffer[bp] >= '0'))
		      NewPopOptions[keyword].value.ivalue = buffer[bp] - '0';
		    else
		      {
			state = 1;
			goto out;
		      }
		    break;
		  case string:
		    state = 6;
		    if (buffer[bp] != '"')
		      {
			state = 1;
			goto out;
		      }
		    i = 0;
		    switch (keyword)
		      {
		      case 3:
			NewPopOptions[3].value.svalue = NewTaskName;
			break;
		      case 4:
			NewPopOptions[4].value.svalue = NewFontName;
			break;
		      case 5:
			NewPopOptions[5].value.svalue = NewCmd;
			break;
		      default:
			state = 1;
			goto out;
		      }
		    break;
		  }
	      break;
	    case 4:
	      if ((buffer[bp] <= 'z') && (buffer[bp] >= 'a'))
		{
		  if (keywordbp >= KEYWORDBUFFERSIZE)
		    {
		      state = 1;
		      goto out;
		    }
		  keywordbuffer[keywordbp++] = buffer[bp] - 32;
		}
	      else if ((buffer[bp] <= 'Z') && (buffer[bp] >= 'A'))
		{
		  if (keywordbp >= KEYWORDBUFFERSIZE)
		    {
		      state = 1;
		      goto out;
		    }
		  keywordbuffer[keywordbp++] = buffer[bp];
		}
	      else if ((buffer[bp] == ' ') || (buffer[bp] == '\t') || (buffer[bp] == '\n'))
		{
		  keywordbuffer[keywordbp] = '\0';
		  NewPopOptions[i].value.ivalue = 2;
		  if (!strcmp ("TRUE", keywordbuffer))
		    NewPopOptions[i].value.ivalue = 1;
		  if (!strcmp ("FALSE", keywordbuffer))
		    NewPopOptions[i].value.ivalue = 0;
		  if (!strcmp ("ON", keywordbuffer))
		    NewPopOptions[i].value.ivalue = 1;
		  if (!strcmp ("OFF", keywordbuffer))
		    NewPopOptions[i].value.ivalue = 0;
		  if (NewPopOptions[i].value.ivalue == 2)
		    {
		      state = 1;
		      goto out;
		    }
		  if (buffer[bp] == '\n')
		    {
		      line++;
		      state = 0;
		    }
		  else
		    state = 7;
		}
	      else
		{
		  state = 1;
		  goto out;
		}
	      break;
	    case 5:
	      if (buffer[bp] == '\n')
		{
		  if (neg)
		    NewPopOptions[keyword].value.ivalue *= -1;
		  if (NewPopOptions[keyword].value.ivalue > NewPopOptions[keyword].max)
		    {
		      state = 1;
		      goto out;
		    }
		  if (NewPopOptions[keyword].value.ivalue < NewPopOptions[keyword].min)
		    {
		      state = 1;
		      goto out;
		    }
		  line++;
		  state = 0;
		}
	      else if ((buffer[bp] == ' ') || (buffer[bp] == '\t'))
		{
		  if (neg)
		    NewPopOptions[keyword].value.ivalue *= -1;
		  if (NewPopOptions[keyword].value.ivalue > NewPopOptions[keyword].max)
		    {
		      state = 1;
		      goto out;
		    }
		  if (NewPopOptions[keyword].value.ivalue < NewPopOptions[keyword].min)
		    {
		      state = 1;
		      goto out;
		    }
		  state = 7;
		}
	      else if ((buffer[bp] <= '9') && (buffer[bp] >= '0'))
		{
		  NewPopOptions[keyword].value.ivalue *= 10;
		  NewPopOptions[keyword].value.ivalue += buffer[bp] - '0';
		}
	      else
		{
		  state = 1;
		  goto out;
		}
	      break;
	    case 6:
	      if ((buffer[bp] == '\n') || (i >= NewPopOptions[keyword].max))
		{
		  state = 1;
		  goto out;
		}
	      NewPopOptions[keyword].value.svalue[i++] = buffer[bp];
	      if (buffer[bp] == '"')
		{
		  NewPopOptions[keyword].value.svalue[--i] = '\0';
		  state = 8;
		}
	      break;
	    case 7:
	      if (buffer[bp] == '#')
		state = 1;
	      else if (buffer[bp] == '\n')
		{
		  line++;
		  state = 0;
		}
	      else if ((buffer[bp] != ' ') && (buffer[bp] != '\t'))
		{
		  state = 1;
		  goto out;
		}
	      break;
	    case 8:
	      if (buffer[bp] == '\n')
		{
		  line++;
		  state = 0;
		}
	      else if ((buffer[bp] != ' ') && (buffer[bp] != '\t'))
		{
		  state = 1;
		  goto out;
		}
	      else
		state = 7;
	      break;
	    default:
	      state = 1;
	      goto out;
	    }
	}
    }
out:

  Close (configFile);
  if (state)
    {
      sprintf (buffer, "NewPop: parse error on line %d of .newpoprc\n", line);
      if (_Backstdout)
	Write (_Backstdout, buffer, strlen (buffer));
      return 1;
    }
  return 0;
}

void 
_main (char *cmd)
{
  USHORT scsifriendinstalled = 0;
  ULONG timersig, LIdle, LDisp;
  BPTR nullfh = NULL;
  USHORT stay = 0;
  struct MsgPort *port;
  struct MsgPort *timerport = NULL;
  struct MsgPort *inputDevPort = NULL;
  struct timerequest *timerreq = NULL;
  struct IOStdReq *inputRequestBlock = NULL;
  struct TextFont *textFont = NULL;
  struct Task *scsifriend;
  struct Interrupt handlerStuff;
  struct Window *titlewindow = NULL;
  struct RastPort *titleRP;
  struct Screen *WBScreen;

  {
    struct TextAttr textAttr =
    {NULL, 0, 0, 0};
    int quitflag = 0;

    global.creatsignum = -1;
    global.mousemovenum = -1;
    global.blanksignum = -1;
    Forbid ();
    if (!(port = FindPort (PORTNAME)))
      {
	stay = 1;
	if (!(port = CreatePort (PORTNAME, 0)))
	  {
	    Permit ();
	    goto abort;
	  }
      }
    Permit ();
    if (cmd && *cmd)
      {
	while (*cmd != ' ')
	  cmd++;
	while (*cmd == ' ')
	  cmd++;
	if ((cmd[0] == '-') && (cmd[1] == 'q') && (cmd[2] == '\n') && (!cmd[3]))
	  {
	    quitflag = 1;
	    if (_Backstdout)
	      Write (_Backstdout, KILLMSG, sizeof (KILLMSG));
	  }
	else
	  {
	    if (_Backstdout)
	      Write (_Backstdout, BANNER, sizeof (BANNER));
	    if (ParseConfigFile ())
	      goto abort;
	  }
      }
    else
      {
	quitflag = !stay;
	if (stay)
	  if (ParseConfigFile ())
	    goto abort;
      }
    textAttr.ta_Name = FONTNAME;
    textAttr.ta_YSize = FONTSIZE;
    global.blankscreen = NULL;
    global.buddy = FindTask (0);
    global.noevents = 0;
    global.key = POPKEY;
    if (quitflag)
      Signal (port->mp_SigTask, SIGBREAKF_CTRL_C);
    if (!stay)
      goto abort;
    if (_Backstdout)
      Close (_Backstdout);
    _Backstdout = NULL;
    if (!(nullfh = Open ("NIL:", MODE_NEWFILE)))
      goto abort;
    if (!(inputDevPort = CreatePort ("NewPopInputPort", 0)))
      goto abort;
    if (!(inputRequestBlock = CreateIOReq (inputDevPort, sizeof (struct IOStdReq))))
      goto abort;
    if (!(timerport = CreatePort ("NewPopTimerPort", 0)))
      goto abort;
    if (!(timerreq = (struct timerequest *) CreateIOReq (timerport, sizeof (struct timerequest))))
      goto abort;
    if ((global.creatsignum = AllocSignal (-1)) == -1)
      goto abort;
    if ((global.blanksignum = AllocSignal (-1)) == -1)
      goto abort;
    if ((global.mousemovenum = AllocSignal (-1)) == -1)
      goto abort;
    if (!(GfxBase = (struct GfxBase *) OpenLibrary ("graphics.library", 0)))
      goto abort;
    if (!(LayersBase = (struct LayersBase *) OpenLibrary ("layers.library", 0)))
      goto abort;
    if (!(IntuitionBase = (struct IntuitionBase *) OpenLibrary ("intuition.library", 0)))
      goto abort;
    if (!(DiskfontBase = (struct DiskfontBase *) OpenLibrary ("diskfont.library", 0)))
      goto abort;
    if (!(ExecBase = (struct ExecBase *) OpenLibrary ("exec.library", 0)))
      goto abort;
    if (OpenDevice (TIMERNAME, UNIT_VBLANK, (struct IORequest *) timerreq, 0))
      goto abort;
    if (OpenDevice ("input.device", 0, (struct IORequest *) inputRequestBlock, 0))
      goto abort;
    if (!NEWPOPLITE)
      {
	Forbid ();
	if (WBScreen = (struct Screen *) OpenWorkBench ())
	  {
	    WindowWidth = SCSIIOMONWIDTH + GAPBETWEEN2 + CPULOADMONWIDTH +
	      GAPBETWEEN + MAXTEXTWIDTH;
	    WindowHeight = WINDOWHEIGHT;
	    NewWindow.LeftEdge = WBScreen->Width - WindowWidth - STARTXOFFSET;
	    NewWindow.TopEdge = 1;
	    NewWindow.Width = WindowWidth;
	    NewWindow.Height = WindowHeight;
	    if (WEWANTWINDOWDRAG)
	      {
		WDragGadget.Height = WindowHeight;
		WDragGadget.Width = WindowWidth;
	      }
	    else
	      NewWindow.FirstGadget = NULL;
	  }
	Permit ();
	if (!(titlewindow = OpenWindow (&NewWindow)))
	  goto abort;
	titleRP = titlewindow->RPort;
	SetAPen (titleRP, 1);
	SetBPen (titleRP, 1);
	SetDrMd (titleRP, JAM2);
	RectFill (titleRP, 0, 0, (WindowWidth - 1), (WindowHeight - 1));
	if (textFont = OpenFont (&textAttr))
	  {
	    if ((textFont->tf_YSize != FONTSIZE) || (textFont->tf_Style != 0))
	      {
		CloseFont (textFont);
		if (!(textFont = OpenDiskFont (&textAttr)))
		  goto abort;
	      }
	  }
	else
	  {
	    if (!(textFont = OpenDiskFont (&textAttr)))
	      goto abort;
	  }
	SetFont (titleRP, textFont);
      }
    handlerStuff.is_Data = (APTR) & global;
    handlerStuff.is_Code = (VOID (*) ()) myhandler;
    handlerStuff.is_Node.ln_Pri = 51;
    timersig = (1 << timerport->mp_SigBit);
    global.creatclisig = 1 << global.creatsignum;
    global.unblanksig = 1 << global.blanksignum;
    global.mousemovesig = 1 << global.mousemovenum;
    inputRequestBlock->io_Command = IND_ADDHANDLER;
    inputRequestBlock->io_Data = (APTR) & handlerStuff;
    DoIO ((struct IORequest *) inputRequestBlock);
    QueueTimer (timerreq, 1 /* 1/60th of a second */ );
    if (!NEWPOPLITE)
      {
	Forbid ();
	if (scsifriend = FindTask (SCSITASKNAME))
	  if (!(scsifriend->tc_Flags & TF_SWITCH))
	    {
	      scsifriend->tc_Switch = scsifriendlosing;
	      scsifriend->tc_Flags |= TF_SWITCH;
	      scsifriendinstalled = 1;
	    }
	Permit ();
	LIdle = ExecBase->IdleCount;
	LDisp = ExecBase->DispCount;
      }
  }

  {
    UWORD newsecond;
    ULONG sig, Seconds, Micros, LastSeconds = 0;
    SHORT instantblank = 0, defeatblank = 0;

    for (;;)
      {
	sig = Wait (global.creatclisig | global.unblanksig | timersig |
		    global.mousemovesig | SIGBREAKF_CTRL_C);

	if (sig & global.mousemovesig)
	  {
	    int left = IntuitionBase->MouseX < 10;
	    int right = (GfxBase->NormalDisplayColumns - IntuitionBase->MouseX) < 10;
	    int upper = IntuitionBase->MouseY < 10;
	    int lower = (GfxBase->NormalDisplayRows - IntuitionBase->MouseY / 2) < 10;

	    instantblank = ((((ULC == 1) && upper) || ((LLC == 1) && lower)) && left) ||
	      ((((URC == 1) && upper) || ((LRC == 1) && lower)) && right);
	    defeatblank = ((((ULC == 2) && upper) || ((LLC == 2) && lower)) && left) ||
	      ((((URC == 2) && upper) || ((LRC == 2) && lower)) && right);
	  }

	CurrentTime (&Seconds, &Micros);
	newsecond = (Seconds != LastSeconds);
	LastSeconds = Seconds;

	if (newsecond && !NEWPOPLITE && !global.blankscreen)
	  {
	    SHORT x;
	    ULONG DIdle, DDisp, Usage;
	    long t;
	    struct tm *p;
	    char *timestr;

	    UpfrontLayer (0L, titlewindow->WLayer);

	    DIdle = ExecBase->IdleCount - LIdle;
	    DDisp = ExecBase->DispCount - LDisp;
	    LIdle = ExecBase->IdleCount;
	    LDisp = ExecBase->DispCount;
	    if (!DDisp)
	      DDisp++;

	    x = 0;
	    if (scsifriendinstalled)
	      {
		Usage = (scsiDisp * 256) / (DDisp + DIdle);
		if (Usage > MAXSCSIUSAGE)
		  Usage = MAXSCSIUSAGE;
		SetAPen (titleRP, 3);
		ScrollRaster (titleRP, 1, 0, x, 0, x + SCSIIOMONWIDTH, (WindowHeight - 1));
		x += SCSIIOMONWIDTH;
		Move (titleRP, x, (WindowHeight - 1));
		Draw (titleRP, x, WindowHeight - ((WindowHeight * Usage) / MAXSCSIUSAGE));
		scsiDisp = 0;
	      }
	    else
	      x += SCSIIOMONWIDTH;

	    /* There be magic below!  Where does 456 come from? :-) */
	    x += GAPBETWEEN2;
	    if (scsiDisp < DDisp)
	      Usage = ((DDisp - scsiDisp) * 456) / (DDisp + DIdle);
	    else
	      Usage = 0;
	    if (Usage < 200)
	      Usage = 0;
	    else
	      Usage = Usage - 200;
	    ScrollRaster (titleRP, 1, 0, x, 0, x + CPULOADMONWIDTH, (WindowHeight - 1));
	    x += CPULOADMONWIDTH;
	    SetAPen (titleRP, 2);
	    Move (titleRP, x, (WindowHeight - 1));
	    Draw (titleRP, x, WindowHeight - ((WindowHeight * Usage) / 256));

	    x += GAPBETWEEN;
	    time (&t);
	    p = localtime (&t);
	    timestr = asctime (p);
	    Move (titleRP, x, STARTYOFFSET);
	    Text (titleRP, timestr, 24);

	    x += TextLength (titleRP, timestr, 24);
	    SetAPen (titleRP, 1);
	    if (x < (WindowWidth - 1))
	      RectFill (titleRP, x, 0, (WindowWidth - 1), (WindowHeight - 1));
	  }

	if (global.blankscreen)
	  {
	    if (NICENEWPOP)
	      {
		if (newsecond)
		  {
		    changecolor ();
		    SetTaskPri (child, 10);
		  }
	      }
	    else
	      {
		if (newsecond)
		  changecolor ();
		updatedisplay ();
	      }
	  }

	if (sig & SIGBREAKF_CTRL_C)
	  {
	    goto abort;
	  }

	if ((sig & global.unblanksig) &&global.blankscreen && !instantblank)
	  {
	    if (NICENEWPOP)
	      killchild ();
	    CloseScreen (global.blankscreen);
	    global.blankscreen = NULL;
	  }

	if (sig & global.creatclisig)
	  {
	    WBenchToFront ();
	    (void) Execute (COMMAND, nullfh, nullfh);
	  }

	if (sig & timersig)
	  {
	    GetMsg (timerport);
	    if (global.blankscreen && NICENEWPOP)
	      QueueTimer (timerreq, 60 /* One second */ );
	    else
	      QueueTimer (timerreq, 1 /* 1/60th of a second*/ );
	    if ((instantblank || (global.noevents++ >= (TIMEOUT * 60))) && !global.blankscreen && !defeatblank)
	      {
		NewScreen.Height = GfxBase->NormalDisplayRows * 2;
		NewScreen.Width = GfxBase->NormalDisplayColumns;
		if (global.blankscreen = OpenScreen (&NewScreen))
		  {
		    xlim = global.blankscreen->Width - 1;
		    ylim = global.blankscreen->Height - 1;
		    blankRP = &(global.blankscreen->RastPort);
		    blankVP = &(global.blankscreen->ViewPort);
		    changecolor ();
		    SetRGB4 (blankVP, 0, 0, 0, 0);
		    SetRGB4 (blankVP, 17, 0, 0, 0);
		    SetRGB4 (blankVP, 18, 0, 0, 0);
		    SetRGB4 (blankVP, 19, 0, 0, 0);
		    SetDrMd (blankRP, JAM1);
		    if (j == 0)
		      {
			x[0] = rand () % xlim + 1;
			if ((x[1] = x[0] + rand () % (xlim / 6) - (xlim / 12)) > xlim)
			  x[1] = xlim;
			if (x[1] < 0)
			  x[1] = 0;
			y[0] = rand () % ylim + 1;
			if ((y[1] = y[0] + rand () % (ylim / 4) - (ylim / 8)) > ylim)
			  y[1] = ylim;
			if (y[1] < 0)
			  y[1] = 0;
			xd[0] = 11;
			yd[0] = 7;
			xd[1] = 3;
			yd[1] = 4;
		      }
		    if (NICENEWPOP)
		      createchild ();
		  }
	      }
	  }

	if (global.blankscreen)
	  ScreenToFront (global.blankscreen);
      }
  }

abort:
  if (stay && port)
    DeletePort (port);
  if (stay && scsifriendinstalled && !NEWPOPLITE)
    {
      Forbid ();
      if (scsifriend = FindTask (SCSITASKNAME))
	if (scsifriend->tc_Switch == scsifriendlosing)
	  {
	    scsifriend->tc_Flags &= !TF_SWITCH;
	    scsifriend->tc_Switch = NULL;
	  }
      Permit ();
    }
  if (stay && titlewindow)
    CloseWindow (titlewindow);
  if (timerreq)
    {
      if (timerreq->tr_node.io_Device)
	CloseDevice ((struct IORequest *) timerreq);
      DeleteIOReq ((struct IOStdReq *) timerreq);
    }
  if (inputRequestBlock)
    {
      if (inputRequestBlock->io_Device)
	{
	  inputRequestBlock->io_Command = IND_REMHANDLER;
	  inputRequestBlock->io_Data = (APTR) & handlerStuff;
	  DoIO ((struct IORequest *) inputRequestBlock);
	  CloseDevice ((struct IORequest *) inputRequestBlock);
	}
      DeleteIOReq (inputRequestBlock);
    }
  if (textFont && !NEWPOPLITE)
    CloseFont (textFont);
  if (timerport)
    DeletePort (timerport);
  if (global.creatsignum != -1)
    FreeSignal (global.creatsignum);
  if (global.blanksignum != -1)
    FreeSignal (global.blanksignum);
  if (global.mousemovenum != -1)
    FreeSignal (global.mousemovenum);
  if (global.blankscreen)
    {
      if (NICENEWPOP)
	killchild ();
      CloseScreen (global.blankscreen);
    }
  if (IntuitionBase)
    CloseLibrary ((struct Library *) IntuitionBase);
  if (GfxBase)
    CloseLibrary ((struct Library *) GfxBase);
  if (LayersBase)
    CloseLibrary ((struct Library *) LayersBase);
  if (DiskfontBase)
    CloseLibrary ((struct Library *) DiskfontBase);
  if (ExecBase)
    CloseLibrary ((struct Library *) ExecBase);
  if (inputDevPort)
    DeletePort (inputDevPort);
  if (_Backstdout)
    Close (_Backstdout);
  if (nullfh)
    Close (nullfh);
}

void 
changecolor (void)
{
  static struct color currentcolor =
  {15, 0, 5}, colordelta =
  {-1, 1, 1};

  switch (rand () % 3)
    {
    case 0:
      currentcolor.red += colordelta.red;
      if (currentcolor.red == 0 ||
	  currentcolor.red == 15)
	colordelta.red = -colordelta.red;
      break;
    case 1:
      currentcolor.green += colordelta.green;
      if (currentcolor.green == 0 ||
	  currentcolor.green == 15)
	colordelta.green = -colordelta.green;
      break;
    case 2:
      currentcolor.blue += colordelta.blue;
      if (currentcolor.blue == 0 ||
	  currentcolor.blue == 15)
	colordelta.blue = -colordelta.blue;
      break;
    }
  SetRGB4 (blankVP, 1, currentcolor.red, currentcolor.green, currentcolor.blue);
}

void 
updatedisplay (void)
{
  SHORT i, k;

  SetAPen (blankRP, 0);
  Move (blankRP, ox[0][j & 15], oy[0][j & 15]);
  Draw (blankRP, ox[1][j & 15], oy[1][j & 15]);
  SetAPen (blankRP, 1);
  Move (blankRP, x[0], y[0]);
  Draw (blankRP, x[1], y[1]);
  for (i = 0; i < 2; i++)
    {
      ox[i][j & 15] = x[i];
      oy[i][j & 15] = y[i];
      x[i] += xd[i];
      y[i] += yd[i];
      if (abs (x[1] - x[0]) > xlim / 4)
	{
	  x[i] -= xd[i] * 4 / 3;
	  xd[i] = -xd[i] / 2;
	}
      if (abs (y[1] - y[0]) > ylim / 3)
	{
	  y[i] -= yd[i] * 4 / 3;
	  yd[i] = -yd[i] / 2;
	}
      if (x[i] < 0)
	{
	  x[i] = 0;
	  xd[i] = -xd[i];
	}
      else if (x[i] > xlim)
	{
	  x[i] = xlim;
	  xd[i] = -xd[i];
	}
      if (y[i] < 0)
	{
	  y[i] = 0;
	  yd[i] = -yd[i];
	}
      else if (y[i] > ylim)
	{
	  y[i] = ylim;
	  yd[i] = -yd[i];
	}
      if (((rand () >> 5) & 127) < 2)
	{
	  if (xd[i] < 1)
	    k = 1;
	  xd[i] = (rand () >> 5) & 7;
	  if (k == (1 - i))
	    xd[i] = -xd[i];
	  k = 0;
	}
      if (((rand () >> 5) & 255) < 50)
	{
	  if (yd[i] < 1)
	    k = 1;
	  yd[i] = (rand () >> 5) & 7;
	  if (k == (1 - i))
	    yd[i] = -yd[i];
	  k = 0;
	}
    }
  ++j;
}

void 
MemCleanup (void)
{
}

void 
QueueTimer (struct timerequest * tr, ULONG ticks)
{
  tr->tr_node.io_Command = TR_ADDREQUEST;
  tr->tr_time.tv_secs = ticks / 60;
  tr->tr_time.tv_micro = (ticks % 60) * 1000000 / 60;
  SendIO ((struct IORequest *) tr);
}

struct MsgPort *
CreatePort (char *name, long pri)
{
  UBYTE sigbit;
  register struct MsgPort *port;

  if ((sigbit = AllocSignal (-1)) == -1)
    return ((struct MsgPort *) 0);
  if (!(port = (struct MsgPort *) AllocMem (sizeof (struct MsgPort), MEMF_CLEAR | MEMF_PUBLIC)))
    {
      FreeSignal (sigbit);
      return ((struct MsgPort *) 0);
    }
  port->mp_Node.ln_Name = name;
  port->mp_Node.ln_Pri = pri;
  port->mp_Node.ln_Type = NT_MSGPORT;
  port->mp_Flags = PA_SIGNAL;
  port->mp_SigBit = sigbit;
  port->mp_SigTask = (struct Task *) FindTask (0);
  if (name)
    AddPort (port);
  return (port);
}

void 
DeletePort (struct MsgPort * port)
{
  if (port->mp_Node.ln_Name)
    RemPort (port);
  FreeSignal (port->mp_SigBit);
  FreeMem ((char *) port, sizeof (struct MsgPort));
}

struct IOStdReq *
CreateIOReq (struct MsgPort * port, int size)
{
  struct IOStdReq *ioReq;

  if (ioReq = (struct IOStdReq *) AllocMem (size, MEMF_CLEAR | MEMF_PUBLIC))
    {
      ioReq->io_Message.mn_Node.ln_Type = NT_MESSAGE;
      ioReq->io_Message.mn_Node.ln_Pri = 0;
      ioReq->io_Message.mn_Length = size;
      ioReq->io_Message.mn_ReplyPort = port;
    }
  return (ioReq);
}

void 
DeleteIOReq (struct IOStdReq * ioReq)
{
  ioReq->io_Message.mn_Node.ln_Type = 0xff;
  ioReq->io_Device = (struct Device *) - 1;
  ioReq->io_Unit = (struct Unit *) - 1;
  FreeMem ((char *) ioReq, ioReq->io_Message.mn_Length);
}
