/*
 * ListPlot.c
 *
 * Yet another plotting filter.
 *
 */
#include <stdio.h>
#include <errno.h>
extern int errno;
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include "datatypes.h"

void	ErrorExit();
char	*calloc();

/* initializations	*/
char	*Usage[] = {
	"Usage: ListPlot [Options]\n",
	0
};
char	*Function[] = {
	"Function: Yet another plotting filter. For help, enter \"ListPlot help\".\n\tFor more verbose help, enter \"ListPlot Verbose=on help=all\".\n",
	"\n",
	"\tListPlot supports a number of different devices.\n",
	"\n",
	"\tListPlot reads from stdin and writes to stdout unless input and/or\n",
	"\toutput files are specified on the command line.\n",
	"\tThe program accepts a file of n-tuples.  Each tuple consists of\n",
	"\ta sequence of space separated numbers terminated with a newline.\n",
	"\tThe first element of each tuple is assumed to be the independent\n",
	"\tvariable.  Each remaining element is plotted against the first to\n",
	"\tproduce n-1 curves.\n",
	"\n",
	"\tListPlot takes a number of command line arguments that may be\n",
	"\tused to control the appearance of the plots.\n",
	"\tFor a list of the arguments enter:\n",
	"\t\tListPlot Help=All.\n",
	"\n",
	"\t'Boolean' values accept values such as true, false,\n",
	"\tyes, no, on, and off. (ex. PlotColor=yes)\n",
	"\n",
	"\t'String' values expect the strings to entered as\n",
	"\tindicated and is case sensitive. (ex. Domain=All)\n",
	"\n",
	"\t'Dbl' values expect real numbers. (ex. AspectRatio=1.0)\n",
	"\n",
	"\t'Interval' values expect a pair of comma separated reals.\n",
	"\t\t(ex. Range=-1.0,3.0)\n",
	"\n",
	"\t'Set' values expect a list of comma separated elements enclosed in\n",
	"\tcurly braces ({}). (ex. LineStyle={MS,MMS,MSmmS})\n",
	"\n",
	"\tThe title and label variables accept strings that include\n",
	"\tthe following control sequences:\n",
	"\t\t#u: move up to superscript position (ended with #d)\n",
	"\t\t#d: move down to subscript position (ended with #u)\n",
	"\t\t#b: backspace to allow overprinting\n",
	"\t\t##: the number symbol\n",
	"\t\t#+: toggle overline mode\n",
	"\t\t#-: toggle underline mode\n",
	"\t\t#gx: Greek letter corresponding to Roman letter x\n",
	"\n",
	0
};

bool	GraphicsInProgress = FALSE;


/* argument definitions	*/
/* GoldenRatio = (1+Sqrt(5))/2	*/
#define	GOLDENRATIO	1.6180339887499
#define ASPECTRATIO	1.0/GOLDENRATIO
char	*H_AngularUnit[] = {
	"\t'AngularUnit' specifies the angular unit of measure\n",
	"\tused for polar plots.  For angles in radians use\n",
	"\t\tAngularUnit=radians\n",
	(char *)NULL
};
VALUE	V_AngularUnit;
ARGDEF	A_AngularUnit = {
		&V_AngularUnit,			/* variable	*/
		"AngularUnit",			/* ID		*/
		"degrees|radians",		/* options	*/
		"string|string",		/* format	*/
		"degrees",			/* default	*/
		"string",			/* Default type */
		H_AngularUnit
	};
char	*H_AnnotationScale[] = {
	"\t'AnnotationScale' controls the size of the characters\n",
	"\tused to annotate the axises.\n",
	(char *)NULL
};
VALUE	V_AnnotationScale;
ARGDEF	A_AnnotationScale = {
		&V_AnnotationScale,		/* variable	*/
		"AnnotationScale",		/* ID		*/
		"dbl",				/* options	*/
		"dbl",				/* format	*/
		"0.50",				/* default	*/
		"dbl",				/* Default type */
		H_AnnotationScale
	};
char	*H_AspectRatio[] = {
	"\t'AspectRatio' controls the relative lengths of the vertical to\n",
	"\thorizontal axes.  The default is 1.0/GoldenRatio.\n",
	"\t\tAspectRatio=0.618\n",
	"\tIf set this value overrides the value of 'ViewPort'.\n",
	(char *)NULL
};
VALUE	V_AspectRatio;
ARGDEF	A_AspectRatio = {
		&V_AspectRatio,			/* variable	*/
		"AspectRatio",			/* ID		*/
		"dbl|Automatic",		/* options	*/
		"dbl|string",			/* format	*/
		"0.61803399",			/* default	*/
		"dbl",				/* Default type */
		H_AspectRatio
	};
char	*H_Boxed[] = {
	"\t'Boxed' adds axes to the edges of the plot.\n",
	"\tIf 'Boxed is given a boolean value then axes are placed on\n",
	"\tall of the edges.  If 'Boxed' is given a string consisting\n",
	"\tof one or more of 't','b','r','l' then axes are added to\n",
	"\ttop, bottom, right and left edges respectively.\n",
	(char *)NULL
};
VALUE	V_Boxed;
ARGDEF	A_Boxed = {
		&V_Boxed,			/* variable	*/
		"Boxed",			/* ID		*/
		"boolean|*",			/* options	*/
		"boolean|string",			/* format	*/
		"yes",				/* default	*/
		"boolean",			/* Default type */
		H_Boxed
	};
char	*H_Domain[] = {
	"\t'Domain' may be used to specify the bounds on the X axis.\n",
	(char *)NULL
};
VALUE	V_Domain;
ARGDEF	A_Domain = {
		&V_Domain,				/* variable	*/
		"Domain",				/* ID		*/
		"interval|All|Automatic",		/* options	*/
		"interval|string|string",		/* format	*/
		"All",					/* default	*/
		"string",				/* Default type */
		H_Domain
	};
char	*H_Gridding[] = {
	"\t'Gridding' puts a grid on the plot.\n",
	(char *)NULL
};
VALUE	V_Gridding;
ARGDEF	A_Gridding = {
		&V_Gridding,			/* variable	*/
		"Gridding",			/* ID		*/
		"boolean",			/* options	*/
		"boolean",			/* format	*/
		"no",				/* default	*/
		"boolean",			/* Default type */
		H_Gridding
	};

/* N.B. Help is treated differently than other variables and its
 * arguments should only be of type "string"!
 */
char	*H_Help[] = {
	"\t'Help' provides some descriptive text for the user-settable\n",
	"\tplotting variables.  If the variable 'Verbose' is set then\n",
	"\tan extended description is provided. To set verbose enter\n",
	"\t\tListPlot Verbose=yes Help=[variable name | All].\n",
	(char *)NULL
};
VALUE	V_Help;
ARGDEF	A_Help = {
		&V_Help,			/* variable	*/
		"Help",				/* ID		*/
		"All|all|*",			/* options	*/
		"string|string|string",		/* format	*/
		"All",				/* default	*/
		"string",			/* Default type */
		H_Help
	};
char	*H_LabelScale[] = {
	"\t'LabelScale' specifies the relative size of the vertical and\n",
	"\thorizontal axis labels.  The typical range is [0.5 - 2.0].\n",
	(char *)NULL
};
VALUE	V_LabelScale;
ARGDEF	A_LabelScale = {
		&V_LabelScale,			/* variable	*/
		"LabelScale",			/* ID		*/
		"dbl",				/* options	*/
		"dbl",				/* format	*/
		"0.85",				/* default*/
		"dbl",				/* Default type */
		H_LabelScale
	};
char	*H_LineColor[] = {
	"\t'LineColor' may be used to specify a list of line colors for\n",
	"\tplots with multiple curves if this feature is supported for\n",
	"\ta particular output device.\n",
	(char *)NULL
};
VALUE	V_LineColor;
ARGDEF	A_LineColor = {
		&V_LineColor,			/* variable	*/
		"LineColor",			/* ID		*/
		"set",				/* options	*/
		"set",				/* format	*/
		"{Black,Red,Green,Blue,Yellow}",	/* default*/
		"set",				/* Default type */
		H_LineColor
	};
char	*H_LineStyle[] = {
	"\t'LineStyle' may be used to specify a list of line styles for\n",
	"\tplots with multiple curves.  Each linestyle is specified as\n",
	"\ta sequence of pen down... pen up elements. The elements are encoded\n",
	"\tusing the following characters to indicate element lengths:\n",
	"\t\t'm':\t250 micron mark\n",
	"\t\t'M':\t4 m's or a 1mm mark\n",
	"\t\t's':\t250 micron space\n",
	"\t\t'S':\t4 s's or a 1mm space\n",
	"\n",
	"\tFor example, a set of line styles might be indicated\n",
	"\t\tLineStyle={MS,MMSS,MMSmmS,mmS,mmSmmSMMS}\n",
	(char *)NULL
};
VALUE	V_LineStyle;
ARGDEF	A_LineStyle = {
		&V_LineStyle,			/* variable	*/
		"LineStyle",			/* ID		*/
		"set",				/* options	*/
		"set",				/* format	*/
		"{MS,MMSS,MMSmmS,mmS,mmSmmSMMS}",/*default*/
		"set",				/* Default type */
		H_LineStyle
	};
char	*H_Origin[] = {
	"\tThe 'Origin' option is not yet implemented. It is intended\n",
	"\tthat this feature beused to specify the origin of the plot.\n",
	(char *)NULL
};
VALUE	V_Origin;
ARGDEF	A_Origin = {
		&V_Origin,			/* variable	*/
		"Origin",			/* ID		*/
		"interval|Automatic|Median",	/* options	*/
		"interval|string|string",	/* format	*/
		"Automatic",			/* default	*/
		"string",			/* Default type */
		H_Origin
	};
char	*H_PlotColor[] = {
	"\t'PlotColor' if TRUE causes the plot to generated in color if this\n",
	"\tfeature is supported on a particular output device.\n",
	(char *)NULL
};
VALUE	V_PlotColor;
ARGDEF	A_PlotColor = {
		&V_PlotColor,			/* variable	*/
		"PlotColor",			/* ID		*/
		"boolean",			/* options	*/
		"boolean",			/* format	*/
		"no",				/* default	*/
		"boolean",			/* Default type */
		H_PlotColor
	};
char	*H_PlotDevice[] = {
	"\t'PlotDevice' specifies output device type.\n",
	(char *)NULL
};
VALUE	V_PlotDevice;
ARGDEF	A_PlotDevice = {
		&V_PlotDevice,			/* variable	*/
		"PlotDevice",			/* ID		*/
		"amiga|printer|iff|hp|aegis|postscript",/* options	*/
		"string|string|string|string|string|string",/* format	*/
		"amiga",			/* default	*/
		"string",			/* Default type */
		H_PlotDevice
	};
char	*H_PlotJoined[] = {
	"\t'PlotJoined' is set connects each data point with a line in the\n",
	"\tcurrent line style. (ex PlotJoined=on )\n",
	(char *)NULL
};
VALUE	V_PlotJoined;
ARGDEF	A_PlotJoined = {
		&V_PlotJoined,			/* variable	*/
		"PlotJoined",			/* ID		*/
		"boolean",			/* options	*/
		"boolean",			/* format	*/
		"yes",				/* default	*/
		"boolean",			/* Default type */
		H_PlotJoined
	};
char	*H_PlotPoints[] = {
	"\t'PlotPoints' if set causes symbols to be plotted in the current\n",
	"\tsymbol style at each data point. (ex. PlotPoints=true )\n",
	(char *)NULL
};
VALUE	V_PlotPoints;
ARGDEF	A_PlotPoints = {
		&V_PlotPoints,			/* variable	*/
		"PlotPoints",			/* ID		*/
		"boolean",			/* options	*/
		"boolean",			/* format	*/
		"false",			/* default	*/
		"boolean",			/* Default type */
		H_PlotPoints
	};
char	*H_PlotTitle[] = {
	"\t'PlotTitle' takes the plot title as an argument.\n",
	"\tGreek symbols may be specified by preceding a character by\n",
	"\t'#g'. Superscripts and subscripts may be specified using\n",
	"\t'#u', '#d' respectively.\n",
	(char *)NULL
};
VALUE	V_PlotTitle;
ARGDEF	A_PlotTitle = {
		&V_PlotTitle,			/* variable	*/
		"PlotTitle",			/* ID		*/
		"*",				/* options	*/
		"string",			/* format	*/
		"",				/* default	*/
		"string",			/* Default type */
		H_PlotTitle
	};
char	*H_PlotType[] = {
	"\t'PlotType' specifies the 'graph paper' upon which the plot is drawn.\n",
	"\tThe current implementation plots in\n",
	"\t\tlinear-linear,\n",
	"\t\tlog-linear,\n",
	"\t\tlinear-log,\n",
	"\t\tlog-log, and\n",
	"\t\tpolar\n",
	"\tformats.\n",
	"\tFor the log type plots, the log of the data is computed\n",
	"\tby ListPlot.\n",
	"\tFor polar plots, see also 'AngularUnit' and 'PolarVariable'.\n",
	(char *)NULL
};
VALUE	V_PlotType;
ARGDEF	A_PlotType = {
		&V_PlotType,			/* variable	*/
		"PlotType",			/* ID		*/
		"linlin|loglin|linlog|loglog|polar",/* options	*/
		"string|string|string|string|string",/* format	*/
		"linlin",			/* default	*/
		"string",			/* Default type */
		H_PlotType
	};
char	*H_PointScale[] = {
	"\t'PointScale' controls the relative size of the data point symbols\n",
	"\twhen data point symbols are being plotted.\n",
	(char *)NULL
};
VALUE	V_PointScale;
ARGDEF	A_PointScale = {
		&V_PointScale,			/* variable	*/
		"PointScale",			/* ID		*/
		"dbl",				/* options	*/
		"dbl",				/* format	*/
		"1.0",				/* default	*/
		"dbl",				/* Default type */
		H_PointScale
	};
char	*H_PointSymbol[] = {
	"\t'PointSymbol' specifies a list of symbols to be used when plotting\n",
	"\tdata points. The symbols types are encoded as integers.\n",
	"\tSee the PLPLOT library documentation to find the symbols\n",
	"\tavailable.\n",
	(char *)NULL
};
VALUE	V_PointSymbol;
ARGDEF	A_PointSymbol = {
		&V_PointSymbol,			/* variable	*/
		"PointSymbol",			/* ID		*/
		"Automatic|set",		/* options	*/
		"string|set",			/* format	*/
		"Automatic",			/* default	*/
		"string",			/* Default type */
		H_PointSymbol
	};
char	*H_PolarVariable[] = {
	"\t'PolarVariable' for polar plots is used to specify\n",
	"\twhether the independent variable is angular or radial.\n",
	(char *)NULL
};
VALUE	V_PolarVariable;
ARGDEF	A_PolarVariable = {
		&V_PolarVariable,		/* variable	*/
		"PolarVariable",		/* ID		*/
		"angle|radius",			/* options	*/
		"string|string",		/* format	*/
		"angle",			/* default	*/
		"string",			/* Default type */
		H_PolarVariable
	};
char	*H_Orientation[] = {
	"\t'Orientation' controls whether the plot is displayed in\n",
	"\tlandscape or portrait orientation.\n",
	(char *)NULL
};
VALUE	V_Orientation;
ARGDEF	A_Orientation = {
		&V_Orientation,				/* variable	*/
		"Orientation",				/* ID		*/
		"portrait|landscape",			/* options	*/
		"string|string",			/* format	*/
		"landscape",				/* default	*/
		"string",				/* Default type */
		H_Orientation
	};
char	*H_Range[] = {
	"\t'Range' specifies the y axis bounds. (ex. Range=-1,1 )\n",
	(char *)NULL
};
VALUE	V_Range;
ARGDEF	A_Range = {
		&V_Range,				/* variable	*/
		"Range",				/* ID		*/
		"interval|All|Automatic",		/* options	*/
		"interval|string|string",		/* format	*/
		"All",					/* default	*/
		"string",				/* Default type */
		H_Range
	};
char	*H_SubPages[] = {
	"\t'SubPages' specifies the number of plots on a pages.\n",
	"\tThis is a vestige of PLPLOT and I am not sure this has any use\n",
	"\twithin the context of 'ListPlot.  It might be useful for\n",
	"\tcontrolling the size of the plots but the 'ViewPort' might be\n",
	"\ta more direct solution.\n",
	(char *)NULL
};
VALUE	V_SubPages;
ARGDEF	A_SubPages = {
		&V_SubPages,				/* variable	*/
		"SubPages",				/* ID		*/
		"interval",				/* options	*/
		"interval",				/* format	*/
		"1.0,1.0",				/* default	*/
		"interval",				/* Default type */
		H_SubPages
	};
char	*H_SupplyAbscissa[] = {
	"\t'SupplyAbscissa' if set causes ListPlot to supply a value for\n",
	"\tthe independent variable.\n",
	(char *)NULL
};
VALUE	V_SupplyAbscissa;
ARGDEF	A_SupplyAbscissa = {
		&V_SupplyAbscissa,			/* variable	*/
		"SupplyAbscissa",			/* ID		*/
		"boolean",				/* options	*/
		"boolean",				/* format	*/
		"no",					/* default	*/
		"boolean",				/* Default type */
		H_SupplyAbscissa
	};
char 	*H_TitleScale[] = {
	"\t'TitleScale' controls the relative size of the title text.\n",
	(char *)NULL
};
VALUE	V_TitleScale;
ARGDEF	A_TitleScale = {
		&V_TitleScale,				/* variable	*/
		"TitleScale",				/* ID		*/
		"dbl",					/* options	*/
		"dbl",					/* format	*/
		"1.0",					/* default	*/
		"dbl",					/* Default type */
		H_TitleScale
	};
char	*H_UseInputFile[] = {
	"\t'UseInputFile' permits the specification of an input file if\n",
	"\tyou would rather not use stdin.\n",
	(char *)NULL
};
VALUE	V_UseInputFile;
ARGDEF	A_UseInputFile = {
		&V_UseInputFile,		/* variable	*/
		"UseInputFile",			/* ID		*/
		"*",				/* options	*/
		"string",			/* format	*/
		"",				/* default	*/
		"string",			/* Default type */
		H_UseInputFile
	};
char	*H_UseOutputFile[] = {
	"\t'UseOutputFile' permits the specification of an output file if\n",
	"\tyou would rather not use stdout.\n",
	(char *)NULL
};
VALUE	V_UseOutputFile;
ARGDEF	A_UseOutputFile = {
		&V_UseOutputFile,		/* variable	*/
		"UseOutputFile",		/* ID		*/
		"*",				/* options	*/
		"string",			/* format	*/
		"",				/* default	*/
		"string",			/* Default type */
		H_UseOutputFile
	};
/* Not used.  V_AspectRatio may be used to control viewport in a nicer way */
char	*H_Verbose[] = {
	"\t'Verbose' if set causes extended messaging.\n",
	(char *)NULL
};
VALUE	V_Verbose;
ARGDEF	A_Verbose = {
		&V_Verbose,			/* variable	*/
		"Verbose",			/* ID		*/
		"boolean",			/* options	*/
		"boolean",			/* format	*/
		"off",				/* default	*/
		"boolean",			/* Default type */
		H_Verbose
	};
char	*H_ViewPort[] = {
	"\t'ViewPort' allows control over the size of a plot. Takes\n",
	"\ta viewing rectangle diagonal as an argument.\n",
	"\t\t(ex. ViewPort=\\{0.1,0.3, 0.9,0.6\\} )\n",
	(char *)NULL
};
VALUE	V_ViewPort;
ARGDEF	A_ViewPort = {
		&V_ViewPort,			/* variable	*/
		"ViewPort",			/* ID		*/
		"rect",				/* options	*/
		"rect",				/* format	*/
		"{0.1,0.1,0.9,0.9}",		/* default	*/
		"rect",				/* Default type */
		H_ViewPort
	};
char	*H_XLabel[] = {
	"\t'XLabel' specifies X axis label.  Greek characters may be included\n",
	"\tby preceding the character with '#g'.  Superscripts and subscripts\n",
	"\tmay be included using '#u' and '#d' control sequences respectively.\n",
	(char *)NULL
};
VALUE	V_XLabel;
ARGDEF	A_XLabel = {
		&V_XLabel,			/* variable	*/
		"XLabel",			/* ID		*/
		"*",				/* options	*/
		"string",			/* format	*/
		"",				/* default	*/
		"string",			/* Default type */
		H_XLabel
	};
char	*H_XTick[] = {
	"\t'XTick' controls the spacing of major axis ticks and the number\n",
	"\tof minor subdivisions. (ex. XTick=0.1,10 indicates major ticks at\n",
	"\tunits of 0.1 with 10 minor subdivisions.)\n",
	(char *)NULL
};
VALUE	V_XTick;
ARGDEF	A_XTick = {
		&V_XTick,			/* variable	*/
		"XTick",			/* ID		*/
		"Automatic|interval",		/* options	*/
		"string|interval",		/* format	*/
		"Automatic",			/* default	*/
		"string",			/* Default type */
		H_XTick
	};
char	*H_YLabel[] = {
	"\t'YLabel' specifies Y axis label.  Greek characters may be included\n",
	"\tby preceding the character with '#g'.  Superscripts and subscripts\n",
	"\tmay be included using '#u' and '#d' control sequences respectively.\n",
	(char *)NULL
};
VALUE	V_YLabel;
ARGDEF	A_YLabel = {
		&V_YLabel,			/* variable	*/
		"YLabel",			/* ID		*/
		"*",				/* options	*/
		"string",			/* format	*/
		"",				/* default	*/
		"string",			/* Default type */
		H_YLabel
	};
char	*H_YTick[] = {
	"\t'YTick' controls the spacing of major axis ticks and the number\n",
	"\tof minor subdivisions. (ex. YTick=0.1,10 indicates major ticks at\n",
	"\tunits of 0.1 with 10 minor subdivisions.)\n",
	(char *)NULL
};
VALUE	V_YTick;
ARGDEF	A_YTick = {
		&V_YTick,			/* variable	*/
		"YTick",			/* ID		*/
		"Automatic|interval",		/* options	*/
		"string|interval",		/* format	*/
		"Automatic",			/* default	*/
		"string",			/* Default type */
		H_YTick
	};
	
ARGDEF	*SymbolTable[] = {
		&A_AngularUnit,
		&A_AnnotationScale,
		&A_AspectRatio,
		&A_Boxed,
		&A_Domain,
		&A_Gridding,
		&A_Help,
		&A_LabelScale,
		&A_LineColor,
		&A_LineStyle,
		&A_Orientation,
		&A_Origin,
		&A_PlotColor,
		&A_PlotJoined,
		&A_PlotPoints,
		&A_PointScale,
		&A_PointSymbol,
		&A_PlotTitle,
		&A_PlotDevice,
		&A_PlotType,
		&A_PolarVariable,
		&A_Range,
		&A_SubPages,
		&A_SupplyAbscissa,
		&A_TitleScale,
		&A_UseInputFile,
		&A_UseOutputFile,
		&A_Verbose,
		&A_ViewPort,
		&A_XLabel,
		&A_YLabel,
		&A_XTick,
		&A_YTick,
		(ARGDEF *)NULL
	};

main(argc, argv)
int	argc;
char	**argv;
{
	register int	i;
	int	NTuples, TupleSize;
	FLOAT	**Data;
	FILE	*Fp;
	void	InterruptHandler();
#ifdef	ANSI_C
	FLOAT	**GetData(FILE *, int *, int *);
	void	ListPlot(FLOAT **,int ,int );
#else
	FLOAT	**GetData();
	void	ListPlot();
#endif


	get_args(argc, argv, SymbolTable, Usage, Function);

	if (VtoBoolean(V_Verbose)) {
		fprintf(stderr, "Arg values:\n");
		for (i=0; SymbolTable[i]; i++) {
			fputc('\t' ,stderr);
			PrintArg(stderr, SymbolTable[i]);
		}
	}


	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
		signal(SIGINT, InterruptHandler);


	if (strlen(V_UseInputFile.val_u.udt_string) > 0) {
		if ((Fp = fopen(V_UseInputFile.val_u.udt_string, "r")) == (FILE *)NULL) {
			perror(V_UseInputFile.val_u.udt_string);
			exit(errno);
		}
	} else {
		Fp = stdin;
	}
	if (strlen(V_UseOutputFile.val_u.udt_string) > 0) {
		if (!freopen(V_UseOutputFile.val_u.udt_string, "w", stdout)) {
			perror(V_UseOutputFile.val_u.udt_string);
			exit(errno);
		}
	}
	if ((Data = GetData(Fp, &NTuples, &TupleSize)) == (FLOAT **)NULL) {
		/* error */
		fprintf(stderr, "Unable to read data...\n");
		ErrorExit();
	}

	ListPlot(Data, NTuples, TupleSize);
}


void
InterruptHandler()
{
	ErrorExit();	/* will not return		*/
	return;   	/* To satisfy some compilers	*/
}


void
ErrorExit()
{
	if (GraphicsInProgress)
		plend();
	exit(0);
}
