/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: simspicerun.c
 * Companion file to simspice.c
 * Written by: Steven M. Rubin, Static Free Software
 *
 * Copyright (c) 2000 Static Free Software.
 *
 * Electric(tm) 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.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Electric(tm); see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, Mass 02111-1307, USA.
 *
 * Static Free Software
 * 4119 Alpine Road
 * Portola Valley, California 94028
 * info@staticfreesoft.com
 */

#include "config.h"
#if SIMTOOL

#include "global.h"
#include "sim.h"
#include "efunction.h"
#include "usr.h"
#include "edialogs.h"
#include <math.h>
#include <errno.h>

#define SP_BUF_SZ 1024

/******************** SPICE NUMBER OUTPUT QUEUE ********************/

#define NONUMBERS  ((NUMBERS *)-1)

typedef struct Inumbers
{
	double time;
	float *list;
	INTBIG count;
	struct Inumbers *nextnumbers;
} NUMBERS;

static NUMBERS *sim_spice_numbersfree = NONUMBERS;

/******************** HSPICE NAME ASSOCIATIONS ********************/

#define NOPA0LINE ((PA0LINE *)-1)

typedef struct Ipa0line
{
	INTBIG   number;
	char    *string;
	struct Ipa0line *nextpa0line;
} PA0LINE;

static PA0LINE *sim_pa0linefree = NOPA0LINE;
static PA0LINE *sim_pa0linefirst = NOPA0LINE;


static char	    sim_spice_facetname[100] = "";		/* Name extracted from .SPO file */
static FILE    *sim_spice_streamfromsim;
static NUMBERS *sim_spice_numbers = NONUMBERS;		/* current list */
static char   **sim_spice_signames;
static INTSML  *sim_spice_sigtypes;
static double  *sim_spice_time;
static float   *sim_spice_val;
static INTBIG   sim_spice_signals = 0;				/* entries in "signames/sigtypes" */
static INTBIG   sim_spice_iter;
static INTBIG   sim_spice_limit = 0;				/* entries in "time/val" */
static INTBIG   sim_spice_filepos;
static INTBIG   sim_spice_filelen;
static BOOLEAN  sim_spice_tr0binary;				/* true if tr0 file is binary */
static char     sim_spice_btr0buf[8192];			/* buffer for binary tr0 data */
static INTBIG   sim_spice_btr0size;					/* size of binary tr0 buffer */
static INTBIG   sim_spice_btr0pos;					/* position in binary tr0 buffer */
static char    *sim_spice_execpars[50];				/* execution parameters */

/* prototypes for local routines */
static NUMBERS *sim_spice_allocnumbers(INTBIG);
static void     sim_spice_freenumbers(NUMBERS*);
static void     sim_spice_terminate(FILE*, char*, char*);
static INTSML   sim_spice_getfromsimulator(void);
static BOOLEAN  sim_spice_getlinefromsimulator(char *line);
static BOOLEAN  sim_spice_topofsignals(char**);
static char    *sim_spice_nextsignals(void);
static BOOLEAN  sim_spice_plotvalues(INTBIG);
static BOOLEAN  sim_spice_ensurespace(INTBIG);
static INTBIG   sim_spice_parseoutput(INTBIG running, FILE *outputfile, INTBIG parsemode, void *dia);
static INTBIG   sim_spice_findsignalname(char *name);
static BOOLEAN  sim_spice_charhandlerschem(WINDOWPART *w, INTSML chr, INTBIG special);
static BOOLEAN  sim_spice_charhandlerwave(WINDOWPART *w, INTSML chr, INTBIG special);
static void     sim_spice_addhighlightednet(BOOLEAN);
static BOOLEAN  sim_spice_readbtr0block(BOOLEAN firstbyteread);
static void     sim_spice_resetbtr0(void);
static INTBIG   sim_spice_gethspicefloat(double *val);
static PA0LINE *sim_allocpa0line(void);
static void     sim_freepa0line(PA0LINE *pl);
static void     sim_spice_removesignal(void);
static void     sim_spice_addarguments(INTBIG index, char *deckname, char *facetname);
static INTBIG   sim_spice_get_double_fromsimulator2(double *d) ;

/*
 * Routine to free all memory allocated by this module.
 */
void sim_freespicerun_memory(void)
{
	REGISTER PA0LINE *pl;

	while (sim_pa0linefirst != NOPA0LINE)
	{
		pl = sim_pa0linefirst;
		sim_pa0linefirst = pl->nextpa0line;
		sim_freepa0line(pl);
	}
	while (sim_pa0linefree != NOPA0LINE)
	{
		pl = sim_pa0linefree;
		sim_pa0linefree = pl->nextpa0line;
		efree((char *)pl);
	}
}

/*
 * routine to allocate a new numbers module from the pool (if any) or memory
 */
NUMBERS *sim_spice_allocnumbers(INTBIG total)
{
	REGISTER NUMBERS *d;

	if (sim_spice_numbersfree == NONUMBERS)
	{
		d = (NUMBERS *)emalloc(sizeof (NUMBERS), sim_tool->cluster);
		if (d == 0) return(NONUMBERS);
	} else
	{
		d = sim_spice_numbersfree;
		sim_spice_numbersfree = (NUMBERS *)d->nextnumbers;
	}

	d->list = (float *)emalloc(((sizeof (float)) * total), sim_tool->cluster);
	if (d->list == 0) return(NONUMBERS);
	d->count = total;
	return(d);
}

/*
 * routine to return numbers module "d" to the pool of free modules
 */
void sim_spice_freenumbers(NUMBERS *d)
{
	efree((char *)d->list);
	d->nextnumbers = sim_spice_numbersfree;
	sim_spice_numbersfree = d;
}

/******************** SPICE EXECUTION AND PLOTTING ********************/

#define MAXTRACES   7
#define MAXLINE   200

PA0LINE *sim_allocpa0line(void)
{
	REGISTER PA0LINE *pl;

	if (sim_pa0linefree == NOPA0LINE)
	{
		pl = (PA0LINE *)emalloc(sizeof (PA0LINE), sim_tool->cluster);
		if (pl == 0) return(NOPA0LINE);
	} else
	{
		pl = sim_pa0linefree;
		sim_pa0linefree = pl->nextpa0line;
	}
	pl->string = 0;
	return(pl);
}

void sim_freepa0line(PA0LINE *pl)
{
	if (pl->string != 0) efree((char *)pl->string);
	pl->nextpa0line = sim_pa0linefree;
	sim_pa0linefree = pl;
}

/*
 * routine to execute SPICE with "infile" as the input deck file, "outfile"
 * as the output listing file.  If "infile" is null, do not run the simulator,
 * but just read "outfile" as the output listing.  If "infile" is not null,
 * run SPICE on that file, presuming that it came from facet "facet".
 */
void sim_spice_execute(char *infile, char *outfile, NODEPROTO *facet)
{
	char line[MAXLINE+5];
	REGISTER INTBIG i, important, parsemode, filetype, simstate;
	NODEPROTO *np;
	FILE *io;
	NUMBERS *num;
	REGISTER FILE *outputfile;
	REGISTER PA0LINE *pl;
	VARIABLE *var;
	char *host, *path, *name, *truename, *pa0file, *pa0truefile, *pt;
	REGISTER void *infstr, *dia;

	/* determine execution/parsing mode */
	var = getvalkey((INTBIG)sim_tool, VTOOL, VINTEGER, sim_dontrunkey);
	if (var == NOVARIABLE) parsemode = SIMRUNNO; else
		parsemode = var->addr;

	/*
	 * Determine the "sim_spice_state".  If we're doing a write-execute-parse,
	 * then we've already done this.  If we're just doing a parse, we have not.
	 */
	var = getvalkey((INTBIG)sim_tool, VTOOL, VINTEGER, sim_spice_statekey);
	if (var != NOVARIABLE) sim_spice_state = var->addr; else sim_spice_state = 0;

	outputfile = NULL;
	sim_spice_filelen = -1;
	if (*infile == 0)
	{
		/* parsing output into simulation window: make sure none is active */
		simstate = sim_window_isactive(&np);
		if ((simstate&SIMWINDOWWAVEFORM) != 0)
		{
#ifndef ACCUMALATE_TRACES
			ttyputerr(_("Only one simulation window can run at a time"));
			ttyputmsg(_("Terminate the current one before reading SPICE output"));
			return;
#endif
		}
		if ((simstate&SIMWINDOWSCHEMATIC) != 0)
			sim_window_stopsimulation();

		/* no input deck, simply use output listing */
		switch (sim_spice_state & SPICETYPE)
		{
			case SPICE2:      filetype = sim_filetypespiceout;    break;
			case SPICE3:      filetype = sim_filetypespiceout;    break;
			case SPICEHSPICE: filetype = sim_filetypehspiceout;   break;
			case SPICEPSPICE: filetype = sim_filetypespiceout;    break;
		}
		sim_spice_streamfromsim = xopen(truepath(outfile), filetype, "", &truename);
		if (sim_spice_streamfromsim == NULL)
		{
			ttyputerr(_("Cannot read %s"), truename);
			return;
		}
		sim_spice_filelen = filesize(sim_spice_streamfromsim);

		/* for HSPICE, get the ".pa0" file with extra name information */
		if ((sim_spice_state & SPICETYPE) == SPICEHSPICE)
		{
			/* free all existing "pa0" information */
			while (sim_pa0linefirst != NOPA0LINE)
			{
				pl = sim_pa0linefirst;
				sim_pa0linefirst = pl->nextpa0line;
				sim_freepa0line(pl);
			}

			infstr = initinfstr();
			addstringtoinfstr(infstr, truepath(outfile));
			pa0file = returninfstr(infstr);
			i = strlen(pa0file) - 4;
			if (namesame(&pa0file[i], ".tr0") == 0)
				strcpy(&pa0file[i], ".pa0");
			io = xopen(pa0file, el_filetypetext, 0, &pa0truefile);
			if (io != NULL)
			{
				for(;;)
				{
					if (xfgets(line, MAXLINE, io)) break;
					pt = line;
					while (*pt == ' ') pt++;
					if (*pt == 0) continue;
					pl = sim_allocpa0line();
					if (pl == NOPA0LINE) break;
					pl->number = atoi(pt);
					while (*pt != ' ' && *pt != 0) pt++;
					while (*pt == ' ') pt++;
					(void)allocstring(&pl->string, pt, sim_tool->cluster);
					for(pt = pl->string; *pt != 0; pt++)
						if (*pt == ' ') break;
					*pt = 0;
					pl->nextpa0line = sim_pa0linefirst;
					sim_pa0linefirst = pl;
				}
				xclose(io);
			}
		}
	} else
	{
		/* input deck exists, run SPICE on it */
		sim_spice_streamfromsim = 0;

		/* for communication with simulator, read fromsim[0] */
		(void)epipe(sim_fromsim);

		sim_process = efork();
		if (sim_process == 1)
		{
			ttyputmsg(_("Run SPICE directly please"));
			return;
		}
		if (sim_process == 0)
		{
			REGISTER INTBIG save0, save1, save2;

			save0 = channelreplacewithfile(0, infile);
			if ((parsemode == SIMRUNYESQPARSE || parsemode == SIMRUNYESQ) &&
				(sim_spice_state & SPICETYPE) == SPICEHSPICE)
			{
				save1 = channelreplacewithfile(1, "/dev/null");
				save2 = channelreplacewithchannel(2, sim_fromsim[1]);
			} else
			{
				save1 = channelreplacewithchannel(1, sim_fromsim[1]);
				save2 = channelreplacewithfile(2, "/dev/null");
			}
			switch (sim_spice_state & SPICETYPE)
			{
				case SPICE2:      name = "spice";    break;
				case SPICE3:      name = "spice3";   break;
				case SPICEHSPICE: name = "hspice";   break;
				case SPICEPSPICE: name = "pspice";   break;
			}
			path = egetenv("ELECTRIC_SPICELOC");
			if (path == NULL) path = SPICELOC;
			host = egetenv("ELECTRIC_REMOTESPICEHOST");
			if (host != NULL)
			{
				/* spice on a remote machine */
				sim_spice_execpars[0] = "rsh";
				sim_spice_execpars[1] = host;
				sim_spice_execpars[2] = path;
				sim_spice_addarguments(3, infile, facet->cell->cellname);
				eexec("/usr/ucb/rsh", sim_spice_execpars);
			} else
			{
				/* spice on the local machine */
				sim_spice_execpars[0] = name;
				sim_spice_addarguments(1, infile, facet->cell->cellname);
				eexec(path, sim_spice_execpars);
			}

			channelrestore(0, save0);
			channelrestore(1, save1);
			channelrestore(2, save2);
			ttyputerr(_("Cannot run %s"), path);
			exit(1);
		}

		/* close the file descriptor so the pipe will terminate when done */
		eclose(sim_fromsim[1]);
		if (parsemode == SIMRUNYESQ && (sim_spice_state & SPICETYPE) == SPICEHSPICE)
			parsemode = SIMRUNYES;

		/* prepare output file if requested */
		if (*outfile != 0)
		{
			char *truename;

			outputfile = xcreate(outfile, sim_filetypespiceout, _("SPICE Output File"), &truename);
			if (outputfile == NULL)
			{
				if (truename != 0) ttyputerr(_("Cannot write SPICE output file %s"), truename);
			}
		}
	}

	/* free any former data */
	if (sim_spice_signals > 0)
	{
		for(i=0; i<sim_spice_signals; i++) efree(sim_spice_signames[i]);
		efree((char *)sim_spice_signames);
		efree((char *)sim_spice_sigtypes);
		sim_spice_signals = 0;
	}
	while (sim_spice_numbers != NONUMBERS)
	{
		num = sim_spice_numbers;
		sim_spice_numbers = sim_spice_numbers->nextnumbers;
		sim_spice_freenumbers(num);
	}

	/* show progress if possible */
	if (sim_spice_filelen > 0)
	{
		dia = DiaInitProgress(0, 0);
		if (dia == 0) return;
		DiaSetProgress(dia, 0, sim_spice_filelen);
	}
	sim_spice_filepos = 0;

	/* if executing and not parsing, simply echo execution progress */
	if ((parsemode == SIMRUNYES || parsemode == SIMRUNYESQ) && *infile != 0)
	{
		for(;;)
		{
			if (sim_spice_getlinefromsimulator(line)) break;
			if (stopping(STOPREASONSPICE)) break;
			if (parsemode == SIMRUNYESQ) continue;
			ttyputmsg("%s", line);
		}
		important = -1;
	} else
	{
		important = sim_spice_parseoutput(*infile != 0, outputfile, parsemode, dia);
	}

	/* clean up */
	if (sim_spice_filelen > 0) DiaDoneProgress(dia);
	(void)sim_spice_terminate(outputfile, outfile, infile);
	if (stopping(STOPREASONSPICE)) return;

	/* plot the list of numbers */
	if (important < 0) return;
#ifdef SHOW_IMPORTANT
	if (sim_spice_plotvalues(important)) ttyputerr(_("Problem making the plot"));
#else
	if (sim_spice_plotvalues(0)) ttyputerr(_("Problem making the plot"));
#endif
}

/*
 * Routine to add execution parameters to the array "sim_spice_execpars" starting at index "index".
 * The name of the facet is in "facetname".
 */
void sim_spice_addarguments(INTBIG index, char *deckname, char *facetname)
{
	REGISTER VARIABLE *var;
	REGISTER char *runargs;
	REGISTER void *infstr;

	var = getvalkey((INTBIG)sim_tool, VTOOL, VSTRING, sim_spice_runargskey);
	if (var == NOVARIABLE)
	{
		sim_spice_execpars[index] = deckname;
		index++;
	} else
	{
		/* parse the arguments */
		runargs = (char *)var->addr;
		for(;;)
		{
			infstr = initinfstr();
			for( ; *runargs != 0 && *runargs != ' '; runargs++)
			{
				if (*runargs == '%') addstringtoinfstr(infstr, facetname); else
					addtoinfstr(infstr, *runargs);
			}
			(void)allocstring(&sim_spice_execpars[index], returninfstr(infstr), sim_tool->cluster);
			index++;
			while (*runargs == ' ') runargs++;
			if (*runargs == 0) break;
		}
	}
	sim_spice_execpars[index] = 0;
}

/*
 * Parse the SPICE output file.  If "running" is nonzero, echo commands (send them
 * to "outputfile" if it is valid).
 * Returns the number of important traces in the data (-1 on error).
 */
INTBIG sim_spice_parseoutput(INTBIG running, FILE *outputfile, INTBIG parsemode, void *dia)
{
	char line[MAXLINE+5], *ptr, *start;
	REGISTER INTBIG i, j, k, l, important, nodcnt, numnoi, rowcount, retval,
		multiplier;
	BOOLEAN past_end, first, datamode, knows;
	float numbers[20];
	REGISTER PA0LINE *pl;
	double lastt, time, value;
	NUMBERS *num, *numend;
	REGISTER void *infstr;
	double d;

	/* parse SPICE rawfile output */
	if ((sim_spice_state & SPICERAWFILE) != 0)
	{
		first = TRUE;
		numend = NONUMBERS;
		sim_spice_signals = -1;
		rowcount = -1;
		for(;;)
		{
			if (stopping(STOPREASONSPICE)) return(-1);
			if (sim_spice_getlinefromsimulator(line)) break;
			if (sim_spice_filelen > 0)
				DiaSetProgress(dia, sim_spice_filepos, sim_spice_filelen);
			if (first)
			{
				/* check the first line for HSPICE format possibility */
				first = FALSE;
				if (strlen(line) >= 20 && line[16] == '9' && line[17] == '0' &&
					line[18] == '0' && line[19] == '7')
				{
					ttyputerr(_("This is an HSPICE file, not a RAWFILE file"));
					ttyputerr(_("Change the SPICE format and reread"));
					return(-1);
				}
			}

			if (running != 0)
			{
				if (outputfile == NULL)
				{
					if (parsemode == SIMRUNYESPARSE) ttyputmsg("%s", line);
				} else sim_spice_xprintf(outputfile, FALSE, "%s\n", line);
			}

			/* find the ":" separator */
			for(ptr = line; *ptr != 0; ptr++) if (*ptr == ':') break;
			if (*ptr == 0) continue;
			*ptr++ = 0;
			while (*ptr == ' ' || *ptr == '\t') ptr++;

			if (namesame(line, "Plotname") == 0)
			{
				if (sim_spice_facetname[0] == '\0')
					strcpy(sim_spice_facetname, ptr);
				continue;
			}

			if (namesame(line, "No. Variables") == 0)
			{
				sim_spice_signals = atoi(ptr) - 1;
				continue;
			}

			if (namesame(line, "No. Points") == 0)
			{
				rowcount = atoi(ptr);
				continue;
			}

			if (namesame(line, "Variables") == 0)
			{
				if (sim_spice_signals < 0)
				{
					ttyputerr(_("Missing variable count in file"));
					return(-1);
				}
				sim_spice_signames = (char **)emalloc(sim_spice_signals * (sizeof (char *)),
					sim_tool->cluster);
				if (sim_spice_signames == 0) return(-1);
				for(i=0; i<=sim_spice_signals; i++)
				{
					if (stopping(STOPREASONSPICE)) return(-1);
					if (sim_spice_getlinefromsimulator(line))
					{
						ttyputerr(_("Error: end of file during signal names"));
						return(-1);
					}
					ptr = line;
					while (*ptr == ' ' || *ptr == '\t') ptr++;
					if (myatoi(ptr) != i)
						ttyputerr(_("Warning: Variable %ld has number %ld"),
							i, myatoi(ptr));
					while (*ptr != 0 && *ptr != ' ' && *ptr != '\t') ptr++;
					while (*ptr == ' ' || *ptr == '\t') ptr++;
					start = ptr;
					while (*ptr != 0 && *ptr != ' ' && *ptr != '\t') ptr++;
					*ptr = 0;
					if (i == 0)
					{
						if (namesame(start, "time") != 0)
							ttyputerr(_("Warning: the first variable should be time, is '%s'"),
								start);
					} else
					{
						(void)allocstring(&sim_spice_signames[i-1], start, sim_tool->cluster);
					}
				}
				continue;
			}
			if (namesame(line, "Values") == 0)
			{
				if (sim_spice_signals < 0)
				{
					ttyputerr(_("Missing variable count in file"));
					return(-1);
				}
				if (rowcount < 0)
				{
					ttyputerr(_("Missing point count in file"));
					return(-1);
				}
				for(j=0; j<rowcount; j++)
				{
					num = sim_spice_allocnumbers(sim_spice_signals);
					if (num == NONUMBERS) return(-1);
					if (numend == NONUMBERS) sim_spice_numbers = num; else
						numend->nextnumbers = num;
					num->nextnumbers = NONUMBERS;
					numend = num;

					if (sim_spice_filelen > 0)
						DiaSetProgress(dia, sim_spice_filepos, sim_spice_filelen);

					for(i=0; i<=sim_spice_signals; i++)
					{
						if (stopping(STOPREASONSPICE)) return(-1);
						if (sim_spice_getlinefromsimulator(line))
						{
							ttyputerr(_("Error: end of file during data points (read %ld out of %ld)"),
								j, rowcount);
							return(-1);
						}
						ptr = line;
						if (i == 0)
						{
							if (myatoi(line) != j)
								ttyputerr(_("Warning: data point %ld has number %ld"),
									j, myatoi(line));
							while(*ptr != 0 && *ptr != ' ' && *ptr != '\t') ptr++;
						}
						while (*ptr == ' ' || *ptr == '\t') ptr++;
						if (i == 0) num->time = atof(ptr); else
							num->list[i-1] = (float)atof(ptr);
					}
				}
			}
			if (namesame(line, "Binary") == 0)
			{
				if (sim_spice_signals < 0)
				{
					ttyputerr(_("Missing variable count in file"));
					return(-1);
				}
				if (rowcount < 0)
				{
					ttyputerr(_("Missing point count in file"));
					return(-1);
				}
				for(j=0; j<rowcount; j++)
				{
					num = sim_spice_allocnumbers(sim_spice_signals);
					if (num == NONUMBERS) return(-1);
					if (numend == NONUMBERS) sim_spice_numbers = num; else
						numend->nextnumbers = num;
					num->nextnumbers = NONUMBERS;
					numend = num;

					if (sim_spice_filelen > 0 )
						DiaSetProgress(dia, sim_spice_filepos, sim_spice_filelen);

					for(i=0; i<=sim_spice_signals; i++)
					{
						if (stopping(STOPREASONSPICE)) return(-1);
						if (sim_spice_get_double_fromsimulator2(&d) == EOF)
						{
							ttyputerr(_("Error: end of file during data points (read %ld out of %ld)"),
								j, rowcount);
							return(-1);
						}
						if (i == 0) num->time = (float)d; else
							num->list[i-1] = (float)d;
					}
				}
			}
		}

		if (sim_spice_signals > 0)
		{
			sim_spice_sigtypes = (INTSML *)emalloc(sim_spice_signals * SIZEOFINTSML, sim_tool->cluster);
			if (sim_spice_sigtypes == 0) return(-1);
		}
		return(sim_spice_signals);
	}
	
	/* handle SPICE 2 format output */
	if ((sim_spice_state & SPICETYPE) == SPICE2)
	{
		datamode = FALSE;
		first = TRUE;
		numend = NONUMBERS;
		for(;;)
		{
			if (stopping(STOPREASONSPICE)) break;
			if (sim_spice_getlinefromsimulator(line)) break;
			if (sim_spice_filelen > 0)
				DiaSetProgress(dia, sim_spice_filepos, sim_spice_filelen);
			if (first)
			{
				/* check the first line for HSPICE format possibility */
				first = FALSE;
				if (strlen(line) >= 20 && line[16] == '9' && line[17] == '0' &&
					line[18] == '0' && line[19] == '7')
				{
					ttyputerr(_("This is an HSPICE file, not a SPICE2 file"));
					ttyputerr(_("Change the SPICE format and reread"));
					return(-1);
				}
			}
			if (running != 0)
			{
				if (outputfile == NULL)
				{
					if (parsemode == SIMRUNYESPARSE)
						ttyputmsg("%s", (isprint(*line) ? line : &line[1]));
				} else sim_spice_xprintf(outputfile, FALSE, "%s\n", line);
			}

			/* look for a facet name */
			if ((sim_spice_facetname[0] == '\0') && (namesamen(line, "*** FACET",9) == 0))
				if ((i = sscanf(line+9,"%s",sim_spice_facetname)) != 1)
					sim_spice_facetname[0] = '\0';
			if (namesamen(line, ".END", 4) == 0 && namesamen(line, ".ENDS", 5) != 0)
			{
				past_end = TRUE;
				continue;
			}
			if (past_end && !datamode)
			{
				if ((isspace(line[0]) || line[0] == '-') && isdigit(line[1]))
					datamode = TRUE;
			}
			if (past_end && datamode)
			{
				if (!((isspace(line[0]) || line[0] == '-') && isdigit(line[1])))
				{
					datamode = FALSE;
					past_end = FALSE;
				}
			}
			if (datamode)
			{
				ptr = line;
				sim_spice_signals = 0;
				for(;;)
				{
					while (isspace(*ptr)) ptr++;
					if (*ptr == 0) break;
					numbers[sim_spice_signals++] = (float)atof(ptr);
					while (*ptr != ' ' && *ptr != 0) ptr++;
				}
				if (sim_spice_signals > 1)
				{
					sim_spice_signals--;
					num = sim_spice_allocnumbers(sim_spice_signals);
					if (num == NONUMBERS) break;
					num->time = numbers[0];
					for(i=0; i<sim_spice_signals; i++) num->list[i] = numbers[i+1];
					if (numend == NONUMBERS) sim_spice_numbers = num; else
						numend->nextnumbers = num;
					num->nextnumbers = NONUMBERS;
					numend = num;
				}
			}
		}

		/* generate dummy names */
		sim_spice_signames = (char **)emalloc(sim_spice_signals * (sizeof (char *)), sim_tool->cluster);
		sim_spice_sigtypes = (INTSML *)emalloc(sim_spice_signals * SIZEOFINTSML, sim_tool->cluster);
		if (sim_spice_signames == 0 || sim_spice_sigtypes == 0)
		{
			/* terminate execution so we can restart simulator */
			return(-1);
		}
		for(i=0; i<sim_spice_signals; i++)
		{
			(void)sprintf(line, "Signal %ld", i+1);
			(void)allocstring(&sim_spice_signames[i], line, sim_tool->cluster);
		}
		return(sim_spice_signals);
	}
	
	/* handle SPICE 3 / PSPICE format output */
	if ((sim_spice_state & SPICETYPE) == SPICE3 ||
		(sim_spice_state & SPICETYPE) == SPICEPSPICE)
	{
		numend = NONUMBERS;
		first = TRUE;
		knows = FALSE;
		for(;;)
		{
			if (stopping(STOPREASONSPICE)) break;
			if (sim_spice_getlinefromsimulator(line)) break;
			if (sim_spice_filelen > 0)
				DiaSetProgress(dia, sim_spice_filepos, sim_spice_filelen);

			if (first)
			{
				/* check the first line for HSPICE format possibility */
				first = FALSE;
				if (strlen(line) >= 20 && line[16] == '9' && line[17] == '0' &&
					line[18] == '0' && line[19] == '7')
				{
					ttyputerr(_("This is an HSPICE file, not a SPICE3/PSPICE file"));
					ttyputerr(_("Change the SPICE format and reread"));
					return(-1);
				}
			}

			/* skip first word if there is an "=" in the line */
			for(ptr = line; *ptr != 0; ptr++) if (*ptr == '=') break;
			if (*ptr == 0) ptr = line; else ptr += 3;

			/* read the data values */
			lastt = 0.0;
			for(;;)
			{
				while (*ptr == ' ' || *ptr == '\t') ptr++;
				if (*ptr == 0 || *ptr == ')') break;
				if (sim_spice_signals == 0)
				{
					num = sim_spice_allocnumbers(MAXTRACES);
					num->time = atof(ptr);
					if (numend == NONUMBERS) sim_spice_numbers = num; else
					{
						numend->nextnumbers = num;
						if (num->time <= lastt && !knows)
						{
							ttyputerr(_("First trace (should be 'time') is not increasing in value"));
							knows = TRUE;
						}
					}
					lastt = num->time;
					num->nextnumbers = NONUMBERS;
					numend = num;
				} else
				{
					if (num == NONUMBERS) ttyputmsg(_("Line %ld of data has too many values"),
						sim_spice_signals); else
					{
						if (sim_spice_signals <= MAXTRACES)
							num->list[sim_spice_signals-1] = (float)atof(ptr);
						num = num->nextnumbers;
					}
				}
				while (*ptr != ' ' && *ptr != '\t' && *ptr != 0) ptr++;
			}

			/* see if there is an ")" at the end of the line */
			if (line[strlen(line)-1] == ')')
			{
				/* advance to the next value for subsequent reads */
				if (sim_spice_signals != 0 && num != NONUMBERS)
					ttyputmsg(_("Line %ld of data has too few values"), sim_spice_signals);
				sim_spice_signals++;
				num = sim_spice_numbers;
			}

			if (running != 0)
			{
				if (outputfile == NULL)
				{
					if (parsemode == SIMRUNYESPARSE)
						ttyputmsg("%s", (isprint(*line) ? line : &line[1]));
				} else sim_spice_xprintf(outputfile, FALSE, "%s\n", line);
			}
		}

		/* generate dummy names */
		sim_spice_signames = (char **)emalloc(sim_spice_signals * (sizeof (char *)), sim_tool->cluster);
		sim_spice_sigtypes = (INTSML *)emalloc(sim_spice_signals * SIZEOFINTSML, sim_tool->cluster);
		if (sim_spice_signames == 0 || sim_spice_sigtypes == 0)
		{
			/* terminate execution so we can restart simulator */
			return(-1);
		}
		for(i=0; i<sim_spice_signals; i++)
		{
			(void)sprintf(line, "Signal %ld", i+1);
			(void)allocstring(&sim_spice_signames[i], line, sim_tool->cluster);
		}
		return(sim_spice_signals);
	}
	
	/* handle HSPICE format output */
	if ((sim_spice_state & SPICETYPE) == SPICEHSPICE)
	{
		/* get number of nodes, special items, and conditions */
		line[4] = 0;
		for(j=0; j<4; j++) line[j] = (char)sim_spice_getfromsimulator();
		nodcnt = atoi(line);
		for(j=0; j<4; j++) line[j] = (char)sim_spice_getfromsimulator();
		numnoi = atoi(line);
		for(j=0; j<4; j++) line[j] = (char)sim_spice_getfromsimulator();
		/* cndcnt = atoi(line); */

		/*
		 * Although this isn't documented anywhere, it appears that the 4th
		 * value on the line is a multiplier for the first, which allows
		 * there to be more than 10000 nodes.
		 */
		for(j=0; j<4; j++) line[j] = (char)sim_spice_getfromsimulator();
		multiplier = atoi(line);
		if (multiplier != 0) nodcnt += multiplier * 10000;

		sim_spice_signals = numnoi + nodcnt - 1;

		/* get version number (known to work with 9007, 9601) */
		for(j=0; j<4; j++) line[j] = (char)sim_spice_getfromsimulator();
		j = atoi(line);

		/* ignore the unused/title information (4+72 characters over line break) */
		for(j=0; j<76; j++)
		{
			k = sim_spice_getfromsimulator();
			if (!sim_spice_tr0binary && k == '\n') j--;
		}

		/* ignore the date/time information (16 characters) */
		for(j=0; j<16; j++) (void)sim_spice_getfromsimulator();

		/* ignore the copywrite information (72 characters over line break) */
		for(j=0; j<72; j++)
		{
			k = sim_spice_getfromsimulator();
			if (!sim_spice_tr0binary && k == '\n') j--;
		}

		/* get number of sweeps */
		line[4] = 0;
		for(j=0; j<4; j++) line[j] = (char)sim_spice_getfromsimulator();
		/* sweepcnt = atoi(line); */

		/* ignore the Monte Carlo information (76 characters over line break) */
		for(j=0; j<76; j++)
		{
			k = sim_spice_getfromsimulator();
			if (!sim_spice_tr0binary && k == '\n') j--;
		}
		if (sim_spice_filelen > 0) DiaSetProgress(dia, sim_spice_filepos, sim_spice_filelen);

		/* get the type of each signal */
		important = numnoi;
		sim_spice_signames = (char **)emalloc(sim_spice_signals * (sizeof (char *)), sim_tool->cluster);
		sim_spice_sigtypes = (INTSML *)emalloc(sim_spice_signals * SIZEOFINTSML, sim_tool->cluster);
		if (sim_spice_signames == 0 || sim_spice_sigtypes == 0)
		{
			/* terminate execution so we can restart simulator */
			return(-1);
		}
		line[8] = 0;
		for(k=0; k<=sim_spice_signals; k++)
		{
			for(j=0; j<8; j++)
			{
				l = sim_spice_getfromsimulator();
				line[j] = (char)l;
				if (!sim_spice_tr0binary && l == '\n') j--;
			}
			if (k == 0) continue;
			if (k < nodcnt) l = k + numnoi - 1; else l = k - nodcnt;
			sim_spice_sigtypes[l] = atoi(line);
		}
		line[16] = 0;
		for(k=0; k<=sim_spice_signals; k++)
		{
			j = 0;
			for(;;)
			{
				l = sim_spice_getfromsimulator();
				if (l == '\n') continue;
				if (l == ' ') break;
				line[j++] = (char)l;
#if 1
				if (j >= MAXLINE) break;
#endif
			}
			line[j] = 0;
			l = (j+15) / 16 * 16 - 1;
			for(; j<l; j++)
			{
				i = sim_spice_getfromsimulator();
				if (!sim_spice_tr0binary && i == '\n') { j--;   continue; }
			}
			if (k == 0) continue;

			/* convert name if there is a colon in it */
			for(j=0; line[j] != 0; j++)
			{
				if (line[j] == ':') break;
				if (!isdigit(line[j])) break;
			}
			if (line[j] == ':')
			{
				l = atoi(line);
				for(pl = sim_pa0linefirst; pl != NOPA0LINE; pl = pl->nextpa0line)
					if (l == pl->number) break;
				if (pl != NOPA0LINE)
				{
					infstr = initinfstr();
					addstringtoinfstr(infstr, pl->string);
					addstringtoinfstr(infstr, &line[j+1]);
					strcpy(line, returninfstr(infstr));
				}
			}

			if (k < nodcnt) l = k + numnoi - 1; else l = k - nodcnt;
			(void)allocstring(&sim_spice_signames[l], line, sim_tool->cluster);
		}

		if (!sim_spice_tr0binary)
		{
			/* finish line, ensure the end-of-header */
			for(j=0; ; j++)
			{
				l = sim_spice_getfromsimulator();
				if (l == '\n') break;
				if (j < 4) line[j] = (char)l;
			}
		} else
		{
			/* gather end-of-header string */
			for(j=0; j<4; j++)
				line[j] = (char)sim_spice_getfromsimulator();
		}
		line[4] = 0;
		if (strcmp(line, "$&%#") != 0)
		{
			ttyputerr(_("HSPICE header improperly terminated"));
			return(-1);
		}
		sim_spice_resetbtr0();

		/* now read the data */
		sim_spice_numbers = numend = NONUMBERS;
		for(;;)
		{
			if (stopping(STOPREASONSPICE)) break;
			if (sim_spice_filelen > 0) DiaSetProgress(dia, sim_spice_filepos, sim_spice_filelen);

			/* get the first number, see if it terminates */
			retval = sim_spice_gethspicefloat(&time);
			if (retval > 0)
			{
				if (sim_spice_tr0binary) break;
				ttyputerr(_("EOF too soon"));
				return(0);
			}
			if (retval < 0) break;

			/* get a row of numbers */
			num = NONUMBERS;
			for(k=1; k<=sim_spice_signals; k++)
			{
				retval = sim_spice_gethspicefloat(&value);
				if (retval > 0)
				{
					if (sim_spice_tr0binary && k == 1) break;
					ttyputerr(_("EOF too soon"));
					return(0);
				}
				if (retval < 0) break;
				if (k == 1)
				{
					/* first valid data point: allocate the structure */
					num = sim_spice_allocnumbers(sim_spice_signals);
					if (numend == NONUMBERS) sim_spice_numbers = num; else
						numend->nextnumbers = num;
					num->nextnumbers = NONUMBERS;
					numend = num;
					num->time = time;
				}
				if (k < nodcnt) l = k + numnoi - 1; else l = k - nodcnt;
				num->list[l] = (float)value;
			}
			if (num == NONUMBERS) break;
		}
		return(important);
	}

	/* unknown format */
	return(-1);
}

/*
 * Routine to read the next floating point number from the HSPICE file into "val".
 * Returns positive on error, negative on EOF, zero if OK.
 */
INTBIG sim_spice_gethspicefloat(double *val)
{
	char line[12];
	REGISTER INTBIG i, j, l;
	union
	{
		unsigned char c[4];
		float x;
	} cast;

	if (!sim_spice_tr0binary)
	{
		line[11] = 0;
		for(j=0; j<11; j++)
		{
			l = sim_spice_getfromsimulator();
			if (l == EOF) return(1);
			line[j] = (char)l;
			if (l == '\n') j--;
		}
		if (strcmp(line, "0.10000E+31") == 0) return(-1);
		*val = atof(line);
		return(0);
	}

	/* binary format */
	for(i=0; i<4; i++)
	{
		l = sim_spice_getfromsimulator();
		if (l == EOF) return(1);
#ifdef BYTES_SWAPPED
		cast.c[i] = (unsigned char)l;
#else
		cast.c[3-i] = (unsigned char)l;
#endif
	}
	*val = cast.x;
	if (*val == 0.10000E+31) return(-1);
	return(0);
}

/* terminate SPICE execution */
void sim_spice_terminate(FILE *outputfile, char *outfile, char *infile)
{
	if (sim_spice_streamfromsim != 0) xclose(sim_spice_streamfromsim); else
		eclose(sim_fromsim[0]);

	if (outputfile != NULL)
	{
		xclose(outputfile);
		ttyputverbose(M_("%s written"), outfile);
	}
	if (*infile != 0)
	{
		if (ekill(sim_process) == 0)
		{
			ewait(sim_process);
			if (errno >= 0)
				ttyputmsg(_("SPICE execution reports error %d"), errno);
					else ttyputmsg(_("SPICE execution terminated"));
		} else ttyputmsg(_("SPICE execution complete"));
		sim_process = -1;
	}
}

/*
 * Routine to reset the binary tr0 block pointer (done between the header and
 * the data).
 */
void sim_spice_resetbtr0(void)
{
	sim_spice_btr0size = 0;
	sim_spice_btr0pos = 0;
}

/*
 * Routine to read the next block of tr0 data.  Skips the first byte if "firstbyteread"
 * is true.  Returns true on EOF.
 */
BOOLEAN sim_spice_readbtr0block(BOOLEAN firstbyteread)
{
	char val;
	unsigned char uval;
	REGISTER INTBIG i, amtread, blocks, bytes, trailer;

	/* read the first word of a binary tr0 block */
	if (!firstbyteread)
	{
		if (xfread(&val, 1, 1, sim_spice_streamfromsim) != 1) return(TRUE);
		sim_spice_filepos++;
	}
	for(i=0; i<3; i++)
		if (xfread(&val, 1, 1, sim_spice_streamfromsim) != 1) return(TRUE);

	/* read the number of 8-byte blocks */
	blocks = 0;
	for(i=0; i<4; i++)
	{
		if (xfread((char *)&uval, 1, 1, sim_spice_streamfromsim) != 1) return(TRUE);
		blocks = (blocks << 8) | uval;
	}

	/* skip the dummy word */
	for(i=0; i<4; i++)
		if (xfread(&val, 1, 1, sim_spice_streamfromsim) != 1) return(TRUE);

	/* read the number of bytes */
	bytes = 0;
	for(i=0; i<4; i++)
	{
		if (xfread((char *)&uval, 1, 1, sim_spice_streamfromsim) != 1) return(TRUE);
		bytes = (bytes << 8) | uval;
	}

	/* now read the data */
	amtread = xfread(sim_spice_btr0buf, 1, bytes, sim_spice_streamfromsim);
	if (amtread != bytes) return(TRUE);

	/* read the trailer count */
	trailer = 0;
	for(i=0; i<4; i++)
	{
		if (xfread((char *)&uval, 1, 1, sim_spice_streamfromsim) != 1) return(TRUE);
		trailer = (trailer << 8) | uval;
	}
	if (trailer != bytes) return(TRUE);

	/* set pointers for the buffer */
	sim_spice_btr0pos = 0;
	sim_spice_btr0size= bytes;
	sim_spice_filepos += 19 + bytes;
	return(FALSE);
}

INTBIG sim_spice_get_double_fromsimulator2(double *d)
{
	INTBIG i;
	if (sim_spice_streamfromsim != 0)
	{
		i = xfread((char *)d, sizeof(double), 1, sim_spice_streamfromsim);
		if (i != 1) return(EOF);
	} else
	{
		i = eread(sim_fromsim[0], (char *)d, (INTBIG)sizeof(double));
		if (i != (INTBIG)sizeof(double)) return(EOF);
	}
	sim_spice_filepos+=(int)sizeof(double);
	return(i);
}

/*
 * Routine to get the next character from the simulator (file or pipe).
 * Returns EOF at end of file.
 */
INTSML sim_spice_getfromsimulator(void)
{
	INTBIG i;
	char val;
#ifdef USE_BUFFER
	static char buf[SP_BUF_SZ];
	static char *bufp = buf;
	static int n=0;
#endif

	if (sim_spice_streamfromsim != 0)
	{
		if ((sim_spice_state & SPICETYPE) == SPICEHSPICE)
		{
			if (sim_spice_filepos == 0)
			{
				/* start of HSPICE file: see if it is binary or ascii */
				i = xfread(&val, 1, 1, sim_spice_streamfromsim);
				if (i != 1) return(EOF);
				sim_spice_filepos++;
				if (val == 0)
				{
					sim_spice_tr0binary = TRUE;
					if (sim_spice_readbtr0block(TRUE)) return(EOF);
				} else
				{
					sim_spice_tr0binary = FALSE;
					return(val);
				}
			}
			if (sim_spice_tr0binary)
			{
				if (sim_spice_btr0pos >= sim_spice_btr0size)
				{
					if (sim_spice_readbtr0block(FALSE))
						return(EOF);
				}
				val = sim_spice_btr0buf[sim_spice_btr0pos];
				sim_spice_btr0pos++;
				return(val&0xFF);
			}
		}
#ifdef USE_BUFFER
		if (n==0) {
			n = xfread(buf, 1, SP_BUF_SZ, sim_spice_streamfromsim);
			bufp=buf;
		}

		if (--n >=0) {
			val = *bufp++;
			i = 1;
		} else i = 0;
#else
		i = xfread(&val, 1, 1, sim_spice_streamfromsim);
#endif

	} else
	{
#ifdef USE_BUFFER
		if (n==0) {
			n = eread(sim_fromsim[0], buf, SP_BUF_SZ);
			bufp=buf;
		}

		if (--n >=0) {
			val = *bufp++;
			i = 1;
		} else i = 0;
#else
		i = eread(sim_fromsim[0], &val, 1);
#endif
	}
	if (i != 1) return(EOF);
	sim_spice_filepos++;
	return(val);
}

/*
 * Routine to get the next line of text from the simulator (file or pipe).
 * Returns true at end of file.
 */
BOOLEAN sim_spice_getlinefromsimulator(char *line)
{
	REGISTER char *ptr;
	REGISTER INTSML ch;

	ptr = line;
	for(;;)
	{
		if (stopping(STOPREASONSPICE)) break;
		ch = sim_spice_getfromsimulator();
		if (ch == EOF) return(TRUE);
		if (ch == '\n' || ch == '\r' || ptr - line >= MAXLINE) break;
		*ptr++ = (char)ch;
	}
	*ptr = 0;
	return(FALSE);
}

BOOLEAN sim_spice_topofsignals(char **c)
{
	sim_spice_iter = 0;
	return(TRUE);
}

char *sim_spice_nextsignals(void)
{
	char *nextname;

	if (sim_spice_iter >= sim_spice_signals) return(0);
	nextname = sim_spice_signames[sim_spice_iter];
	sim_spice_iter++;
	return(nextname);
}

static COMCOMP sim_spice_picktrace = {NOKEYWORD, sim_spice_topofsignals,
	sim_spice_nextsignals, NOPARAMS, 0, " \t", N_("pick a signal to display"), ""};

/*
 * routine to display the numbers in "sim_spice_numbers".  For each entry of the
 * linked list is another time sample, with "sim_spice_signals" trace points.  The
 * names of these signals is in "sim_spice_signames".  Only the first "important"
 * entries should be displayed initially.  Returns true on error.
 */
BOOLEAN sim_spice_plotvalues(INTBIG important)
{
	REGISTER INTBIG i, numtotal, j, k, tr, position, numtraces;
	INTBIG oldsigcount;
	double min, max;
	char *pars[3], **oldsignames, *pt, *start;
	REGISTER NODEPROTO *plotnp;
	REGISTER VARIABLE *var;
	REGISTER NUMBERS *num;
	void *sa;
	extern COMCOMP us_yesnop, us_showdp;
	REGISTER void *infstr;

	/* count the number of values */
	for(num = sim_spice_numbers, numtotal=0; num != NONUMBERS; num = num->nextnumbers)
		numtotal++;
	if (numtotal == 0) return(FALSE);

	/* we have to establish a link for the plot facet */
	plotnp = NONODEPROTO;

	/* if we're already simulating a facet, use that one */
	if (sim_simnt != NONODEPROTO) plotnp = sim_simnt; else
		if (sim_spice_facetname[0] != '\0') plotnp = getnodeproto(sim_spice_facetname);
	if (plotnp != NONODEPROTO && plotnp->primindex != 0) plotnp = NONODEPROTO;
	if (plotnp != NONODEPROTO)
	{
		infstr = initinfstr();
		formatinfstr(infstr, _("Is this a simulation of facet %s?"),
			describenodeproto(plotnp));
		i = ttygetparam(returninfstr(infstr), &us_yesnop, 3, pars);
		if (i == 1)
		{
			if (pars[0][0] == 'n') plotnp = NONODEPROTO;
		}
	}

	/* one final chance to pick a facet */
	if (plotnp == NONODEPROTO)
	{
		i = ttygetparam(_("Please select a facet to associate with this plot: "),
			&us_showdp, 3, pars);
		if (i > 0) plotnp = getnodeproto(pars[0]);
	}
	if (plotnp == NONODEPROTO) return(TRUE);

	/* get memory for the values */
	if (sim_spice_ensurespace(numtotal)) return(TRUE);

	j = 0;
	for(num = sim_spice_numbers; num != NONUMBERS; num = num->nextnumbers)
	{
		sim_spice_time[j++] = num->time;
	}

	/* find former signal order */
	oldsigcount = 0;
	numtraces = 1;
	var = getvalkey((INTBIG)plotnp, VNODEPROTO, VSTRING|VISARRAY, sim_window_signalorder_key);
	if (var != NOVARIABLE)
	{
		oldsigcount = getlength(var);
		if (oldsigcount > 0)
		{
			sa = newstringarray(sim_tool->cluster);
			for(i=0; i<oldsigcount; i++)
			{
				pt = ((char **)var->addr)[i];
				addtostringarray(sa, pt);

				start = pt;
				if (*pt == '-') pt++;
				for( ; *pt != 0; pt++)
					if (!isdigit(*pt) || *pt == ':') break;
				if (*pt != ':') continue;
				{
					position = atoi(start) + 1;
					if (position > numtraces) numtraces = position;
				}
			}
			oldsignames = getstringarray(sa, &oldsigcount);
		}
	}

	/* make the simulation window */
	if (sim_window_create(numtraces, plotnp, sim_spice_charhandlerwave,
		sim_spice_charhandlerschem, SPICE)) return(TRUE);

	/* add in saved signals */
	for(j=0; j<oldsigcount; j++)
	{
		/* see if the name is a bus, and ignore it */
		for(pt = oldsignames[j]; *pt != 0; pt++) if (*pt == '\t') break;
		if (*pt == '\t') continue;

		/* a single signal */
		position = 0;
		pt = oldsignames[j];
		if (*pt == '-') pt++;
		for( ; *pt != 0; pt++)
			if (!isdigit(*pt) || *pt == ':') break;
		if (*pt != ':') pt = oldsignames[j]; else
		{
			position = atoi(oldsignames[j]);
			pt++;
		}
		for(i=0; i<sim_spice_signals; i++)
		{
			if (namesame(sim_spice_signames[i], pt) != 0) continue;
			tr = sim_window_newtrace(position, sim_spice_signames[i], 0);
			k = 0;
			for(num = sim_spice_numbers; num != NONUMBERS; num = num->nextnumbers)
				sim_spice_val[k++] = num->list[i];
			sim_window_loadanatrace(tr, numtotal, sim_spice_time, sim_spice_val);
			break;
		}
	}

	for(i=0; i<important; i++)
	{
		if (sim_window_findtrace(sim_spice_signames[i]) != 0) continue;
		tr = sim_window_newtrace(0, sim_spice_signames[i], 0);
		k = 0;
		for(num = sim_spice_numbers; num != NONUMBERS; num = num->nextnumbers)
			sim_spice_val[k++] = num->list[i];
		sim_window_loadanatrace(tr, numtotal, sim_spice_time, sim_spice_val);
	}

	sim_window_auto_anarange();

	/* clean up */
	min = sim_spice_time[0];   max = sim_spice_time[numtotal-1];
	if (min >= max)
	{
		ttyputmsg(_("Invalid time range"));
		return(TRUE);
	}
	sim_window_settimerange(0, min, max);
	sim_window_setmaincursor((max-min)*0.25f + min);
	sim_window_setextensioncursor((max-min)*0.75f + min);
	if (oldsigcount > 0) killstringarray(sa);
	sim_window_redraw();
	return(FALSE);
}

/*
 * routine to ensure that there is space for "count" time samples in the global arrays
 * "sim_spice_time" and "sim_spice_val".  Returns true on error.
 */
BOOLEAN sim_spice_ensurespace(INTBIG count)
{
	REGISTER INTBIG take;

	if (count <= sim_spice_limit) return(FALSE);
	if (sim_spice_limit > 0)
	{
		efree((char *)sim_spice_time);
		efree((char *)sim_spice_val);
		sim_spice_limit = 0;
	}
	take = count + 10;
	sim_spice_time = (double *)emalloc(take * (sizeof (double)), sim_tool->cluster);
	if (sim_spice_time == 0) return(TRUE);
	sim_spice_val = (float *)emalloc(take * (sizeof (float)), sim_tool->cluster);
	if (sim_spice_val == 0) return(TRUE);
	sim_spice_limit = take;
	return(FALSE);
}

BOOLEAN sim_spice_charhandlerschem(WINDOWPART *w, INTSML chr, INTBIG special)
{
	char *par[3];

	/* special characters are not handled here */
	if (special != 0)
		return(us_charhandler(w, chr, special));

	switch (chr)
	{
		case '?':		/* help */
			ttyputmsg(_("These keys may be typed in the SPICE waveform window:"));
			ttyputinstruction(" d", 6, _("move down the hierarchy"));
			ttyputinstruction(" u", 6, _("move up the hierarchy"));
			ttyputinstruction(" a", 6, _("add selected network to waveform window in new frame"));
			ttyputinstruction(" o", 6, _("overlay selected network, add to current frame"));
			ttyputinstruction(" r", 6, _("remove selected network from waveform window"));
			return(FALSE);
		case 'a':		/* add trace */
			sim_spice_addhighlightednet(FALSE);
			return(FALSE);
		case 'o':		/* overlap trace */
			sim_spice_addhighlightednet(TRUE);
			return(FALSE);
		case 'r':		/* remove trace */
		case DELETEKEY:
			sim_spice_removesignal();
			return(FALSE);
		case 'd':		/* move down the hierarchy */
			us_editfacet(0, par);
			return(FALSE);
		case 'u':		/* move up the hierarchy */
			us_outhier(0, par);
			return(FALSE);
	}
	return(us_charhandler(w, chr, special));
}

BOOLEAN sim_spice_charhandlerwave(WINDOWPART *w, INTSML chr, INTBIG special)
{
	char *par[3];
	REGISTER INTBIG tr, i, j, thispos, frame;
	REGISTER NUMBERS *num;

	/* special characters are not handled here */
	if (special != 0)
		return(us_charhandler(w, chr, special));

	/* see if there are special functions for SPICE waveform simulation */
	switch (chr)
	{
		case '?':		/* help */
			ttyputmsg(_("These keys may be typed in the SPICE Waveform window:"));
			ttyputinstruction(" 9", 6, _("show entire vertical range"));
			ttyputinstruction(" 7", 6, _("zoom vertical range in"));
			ttyputinstruction(" 0", 6, _("zoom vertical range out"));
			ttyputinstruction(" 8", 6, _("shift vertical range up"));
			ttyputinstruction(" 2", 6, _("shift vertical range down"));
			ttyputmsg(_("(Use standard window scaling commands for the horizontal axis)"));
			ttyputinstruction(" a", 6, _("add signal to simulation window in a new frame"));
			ttyputinstruction(" o", 6, _("overlay signal, added to current frame"));
			ttyputinstruction(" r", 6, _("remove selected signal from simulation window"));
			ttyputinstruction(" e", 6, _("remove all traces"));
			ttyputinstruction(" p", 6, _("preserve snapshot of simulation window in database"));
			return(FALSE);
		case '9':		/* show entire vertical range */
			tr = sim_window_gethighlighttrace();
			sim_window_auto_anarange() ;
			sim_window_redraw();
			return(FALSE);
		case '7':		/* zoom vertical range in */
			tr = sim_window_gethighlighttrace();
			frame = sim_window_gettraceframe(tr);
			sim_window_zoom_frame(frame);
			sim_window_redraw();
			return(FALSE);
		case '0':		/* zoom vertical range out */
			tr = sim_window_gethighlighttrace();
			frame = sim_window_gettraceframe(tr);
			sim_window_zoomout_frame(frame);
			sim_window_redraw();
			return(FALSE);
		case '8':		/* shift vertical range up */
			tr = sim_window_gethighlighttrace();
			frame = sim_window_gettraceframe(tr);
			sim_window_shiftup_frame(frame);
			sim_window_redraw();
			return(FALSE);
		case '2':		/* shift vertical range down */
			tr = sim_window_gethighlighttrace();
			frame = sim_window_gettraceframe(tr);
			sim_window_shiftdown_frame(frame);
			sim_window_redraw();
			return(FALSE);
		case 'p':		/* preserve image in {simulation_snapshot} view */
			sim_window_savegraph();
			return(FALSE);
		case 'a':		/* add trace */
			i = ttygetparam(_("Signal to add"), &sim_spice_picktrace, 3, par);
			if (i == 0) return(FALSE);
			i = sim_spice_findsignalname(par[0]);
			if (i < 0) return(FALSE);

			/* create a new trace in the last slot */
			tr = sim_window_newtrace(-1, sim_spice_signames[i], 0);
			j = 0;
			for(num = sim_spice_numbers; num != NONUMBERS; num = num->nextnumbers) {
				sim_spice_val[j++] = num->list[i];
			}
			sim_window_loadanatrace(tr, j, sim_spice_time, sim_spice_val);
			sim_window_cleartracehighlight();
			sim_window_addhighlighttrace(tr);
			sim_window_auto_anarange();
			sim_window_redraw();
			return(FALSE);
		case 'o':		/* overlap trace */
			tr = sim_window_gethighlighttrace();
			if (tr == 0) return(FALSE);
			thispos = sim_window_gettraceframe(tr);
			i = ttygetparam(_("Signal to overlay"), &sim_spice_picktrace, 3, par);
			if (i == 0) return(FALSE);
			i = sim_spice_findsignalname(par[0]);
			if (i < 0) return(FALSE);

			/* create a new trace in this slot */
			tr = sim_window_newtrace(thispos, sim_spice_signames[i], 0);
			j = 0;
			for(num = sim_spice_numbers; num != NONUMBERS; num = num->nextnumbers) {
				sim_spice_val[j++] = num->list[i];
			}
			sim_window_loadanatrace(tr, j, sim_spice_time, sim_spice_val);
			sim_window_cleartracehighlight();
			sim_window_addhighlighttrace(tr);
			sim_window_auto_anarange();
			sim_window_redraw();
			return(FALSE);
		case 'r':		/* remove trace */
		case DELETEKEY:
			sim_spice_removesignal();
			return(FALSE);
		case 'e':
			sim_window_killalltraces(FALSE);
			sim_window_redraw();
			return(FALSE);
	}
	return(us_charhandler(w, chr, special));
}

/*
 * Remove the highlighted signal from the waveform window.
 */
void sim_spice_removesignal(void)
{
	REGISTER INTBIG tr, trl, *trs, i, thispos, pos, lines;

	trs = sim_window_gethighlighttraces();
	if (trs[0] == 0) return;
	sim_window_cleartracehighlight();
	for(i=0; trs[i] != 0; i++)
	{
		tr = trs[i];

		/* see if any other traces use this line */
		thispos = sim_window_gettraceframe(tr);
		sim_window_inittraceloop();
		for(;;)
		{
			trl = sim_window_nexttraceloop();
			if (trl == tr) continue;
			if (trl == 0) break;
			pos = sim_window_gettraceframe(trl);
			if (pos == thispos) break;
		}
		lines = sim_window_getnumframes();
		if (trl == 0 && lines > 1)
		{
			/* no other traces on this line, delete it */
			sim_window_inittraceloop();
			for(;;)
			{
				trl = sim_window_nexttraceloop();
				if (trl == 0) break;
				pos = sim_window_gettraceframe(trl);
				if (pos > thispos) sim_window_settraceframe(trl, pos-1);
			}
			sim_window_setnumframes(lines-1);
		}

		/* kill trace, redraw */
		sim_window_killtrace(tr);
	}
	sim_window_redraw();
}

/*
 * Routine to add the highlighted signal to the waveform window.
 */
void sim_spice_addhighlightednet(BOOLEAN overlay)
{
	REGISTER ARCINST *ai;
	NODEPROTO *np;
	REGISTER INTBIG i, tr, frameno, j;
	REGISTER char *pt;
	REGISTER NUMBERS *num;

	if ((sim_window_isactive(&np) & SIMWINDOWWAVEFORM) == 0)
	{
		ttyputerr(_("Not displaying a waveform"));
		return;
	}

	ai = (ARCINST *)asktool(us_tool, "get-arc");
	if (ai == NOARCINST)
	{
		ttyputerr(_("Select an arc first"));
		return;
	}
	if (ai->network == NONETWORK)
	{
		ttyputerr(_("This arc has no network information"));
		return;
	}
	pt = sim_spice_signalname(ai->network);
	if (pt == 0)
	{
		ttyputerr(_("Cannot get SPICE signal for network %s"), pt);
		return;
	}
	i = sim_spice_findsignalname(pt);
	if (i < 0)
	{
		ttyputerr(_("Cannot find network %s in the simulation data"), pt);
		return;
	}

	/* figure out where to show the new signal */
	if (overlay)
	{
		frameno = sim_window_getcurframe();
		if (frameno < 0) overlay = FALSE;
	}
	if (!overlay) frameno = -1; else
	{
		frameno = sim_window_getcurframe();
		sim_window_cleartracehighlight();
	}

	/* create a new trace in this slot */
	tr = sim_window_newtrace(frameno, sim_spice_signames[i], 0);
	j = 0;
	for(num = sim_spice_numbers; num != NONUMBERS; num = num->nextnumbers)
	{
		sim_spice_val[j++] = num->list[i];
	}
	sim_window_loadanatrace(tr, j, sim_spice_time, sim_spice_val);
	sim_window_auto_anarange();
	sim_window_redraw();
	sim_window_cleartracehighlight();
	sim_window_addhighlighttrace(tr);
}

/*
 * Routine to return the NETWORK associated with HSPICE signal name "name".
 */
NETWORK *sim_spice_networkfromname(char *name)
{
	NODEPROTO *np;
	REGISTER NODEPROTO *cnp;
	REGISTER VARIABLE *var;
	REGISTER char *pt, *start;
	REGISTER NODEINST *ni;
	REGISTER NETWORK *net;

	/* get simulation facet, quit if not simulating */
	if (sim_window_isactive(&np) == 0) return(NONETWORK);

	/* parse the names, separated by dots */
	start = name;
	for(;;)
	{
		for(pt = start; *pt != 0 && *pt != '.'; pt++) ;
		if (*pt == 0) break;

		/* get node name that is pushed into */
		*pt = 0;
		if (*start == 'x') start++;
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
			if (var == NOVARIABLE) continue;
			if (namesame(start, (char *)var->addr) == 0) break;
		}
		*pt++ = '.';
		if (ni == NONODEINST) return(NONETWORK);
		start = pt;
		np = ni->proto;
		cnp = contentsview(np);
		if (cnp != NONODEPROTO) np = cnp;
	}

	/* find network "start" in facet "np" */
	net = getnetwork(start, np);
	return(net);
}

/*
 * Routine to return the SPICE network name of network "net".
 */
char *sim_spice_signalname(NETWORK *net)
{
	NODEPROTO *np;
	REGISTER char *prevstr;
	REGISTER NODEPROTO *netpar;
	REGISTER VARIABLE *varname;
	REGISTER PORTPROTO *pp;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER NODEINST *ni;
	REGISTER void *infstr;
	INTBIG index;

	/* get simulation facet, quit if not simulating */
	if (sim_window_isactive(&np) == 0) return(0);

	/* shift this network up the hierarchy as far as it is exported */
	for(;;)
	{
		/* if this network is at the current level of hierarchy, stop going up the hierarchy */
		if (net->parent == np) break;

		/* find the instance that is the proper parent of this facet */
		ni = descentparent(net->parent, &index);
		if (ni == NONODEINST) break;

		/* see if the network is exported from this level */
		for(pp = net->parent->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
			if (pp->network == net) break;
		if (pp != NOPORTPROTO)
			pp = equivalentport(net->parent, pp, ni->proto);
		if (pp == NOPORTPROTO) break;

		/* exported: find the network in the higher level */
		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
			if (pi->proto == pp)
		{
			net = pi->conarcinst->network;
			break;
		}
		if (pi == NOPORTARCINST)
		{
			for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
				if (pe->proto == pp)
			{
				net = pe->exportproto->network;
				break;
			}
			if (pe == NOPORTEXPINST) break;
		}
	}

	/* construct a path to the top-level of simulation from the net's current level */
	netpar = net->parent;
	prevstr = "";
	for(;;)
	{
		if (netpar->cell == np->cell) break;

		/* find the instance that is the proper parent of this facet */
		ni = descentparent(netpar, &index);
		if (ni == NONODEINST) break;

		varname = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
		if (varname == 0)
		{
			ttyputerr(_("Back annotation is missing"));
			return(0);
		}
		infstr = initinfstr();
		addtoinfstr(infstr, 'x');
		addstringtoinfstr(infstr, (char *)varname->addr);
		if (*prevstr != 0)
		{
			addtoinfstr(infstr, '.');
			addstringtoinfstr(infstr, prevstr);
		}
		prevstr = returninfstr(infstr);
		netpar = ni->parent;
	}

	infstr = initinfstr();
	addstringtoinfstr(infstr, prevstr);
	if (*prevstr != 0) addtoinfstr(infstr, '.');
	if (net->namecount <= 0) addstringtoinfstr(infstr, describenetwork(net)); else
		addstringtoinfstr(infstr, net->netname);
	return(returninfstr(infstr));
}

INTBIG sim_spice_findsignalname(char *name)
{
	REGISTER INTBIG i;

	for(i=0; i<sim_spice_signals; i++)
		if (namesame(name, sim_spice_signames[i]) == 0) return(i);
	return(-1);
}

#endif  /* SIMTOOL - at top */
