magic/netmenu/NMnetlist.c

1020 lines
26 KiB
C

/* NMnetlist.c -
*
* This file manages netlists for the Magic netlist menu
* package. It reads and writes netlists, and provides
* routines to modify the nets.
*
* *********************************************************************
* * Copyright (C) 1985, 1990 Regents of the University of California. *
* * Permission to use, copy, modify, and distribute this *
* * software and its documentation for any purpose and without *
* * fee is hereby granted, provided that the above copyright *
* * notice appear in all copies. The University of California *
* * makes no representations about the suitability of this *
* * software for any purpose. It is provided "as is" without *
* * express or implied warranty. Export of this software outside *
* * of the United States of America may require an export license. *
* *********************************************************************
*/
#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/netmenu/NMnetlist.c,v 1.2 2010/09/12 23:36:13 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include "utils/magic.h"
#include "utils/utils.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "windows/windows.h"
#include "utils/main.h"
#include "textio/textio.h"
#include "netmenu/nmInt.h"
#include "utils/undo.h"
#include "utils/malloc.h"
#include "netmenu/netmenu.h"
/* The data structure below is used to describe each of the
* netlist files currently known to Magic. At any given time,
* only one is "current". A netlist is not much more than a
* big hash table of terminal names. Some of the entries in
* the hash table have null values (these correspond to terminals
* that have been deleted). Where a hash table entry has a
* non-zero value, it points to a NetEntry, which links together
* all of the terminals in a net.
*/
/* Netlist structure: one of these per netlist known to Magic. */
typedef struct xxx_nl_1
{
char *nl_name; /* Name of the netlist file, before
* path expansion.
*/
char *nl_fileName; /* Actual path-expanded file name
* (place to write back the netlist.
*/
HashTable nl_table; /* Hash table holding nets. */
int nl_flags; /* Various flag bits; see below. */
struct xxx_nl_1 *nl_next; /* All netlists are linked together
* in one big long list.
*/
} Netlist;
/* Flag bits for Netlist:
*
* NL_MODIFIED: 1 means this netlist has been modified since
* the last time it was written to disk.
*/
#define NL_MODIFIED 1
/* NetEntry structure: one of these for each terminal in each
* net. The hash table entry for the terminal points to this
* structure. Double links are used to tie together all entries
* for one net into a circular structure.
*/
typedef struct xxx_ne_1
{
char *ne_name; /* Pointer to name of terminal (this
* points to the text string in the
* hash table entry.
*/
int ne_flags; /* Various flags, see below. */
struct xxx_ne_1 *ne_next; /* Next entry for this net. */
struct xxx_ne_1 *ne_prev; /* Previous entry for this net. */
} NetEntry;
/* The flags currently used in NetEntry's are:
*
* NETENTRY_SEEN: Used to keep this entry from being processed twice
* when enumerating nets.
*/
#define NETENTRY_SEEN 1
Netlist *nmCurrentNetlist = NULL; /* The netlist all procedures operate
* on for now.
*/
Netlist *nmListHead = NULL; /* The first netlist in the linked
* list of all netlists.
*/
/* Used in asking the user for confirmation: */
static const char * const yesno[] = {"no", "yes", NULL};
/*
* ----------------------------------------------------------------------------
*
* NMAddTerm --
*
* This adds a terminal to the same net as another given terminal.
*
* Results:
* A pointer is returned to the name of the terminal that has
* been added. This is a pointer to the hash table entry, so
* it's not going to go away until the hash table is explictly
* deleted. This is a convenience to provide callers with a
* handle they can use later to refer to this net. If no
* terminal was added, either because other is NULL or because
* there isn't a current netlist, NULL is returned.
*
* Side effects:
* If new is already on a net, it is removed from that net.
* If other doesn't exist in the table, an entry is created
* for it. Then new is added to the net containing other.
* If new and other are the same, a new net is created with
* just one terminal.
*
* ----------------------------------------------------------------------------
*/
char *
NMAddTerm(new, other)
char *new; /* Name of new terminal to be added. */
char *other; /* Name of terminal whose net new is to join.*/
{
HashEntry *hNew, *hOther;
NetEntry *newEntry, *otherEntry;
/* Lookup new, and remove it from its current net, if there is one. */
if ((nmCurrentNetlist == NULL) || (new == NULL) || (other == NULL))
return NULL;
nmCurrentNetlist->nl_flags |= NL_MODIFIED;
hNew = HashFind(&nmCurrentNetlist->nl_table, new);
newEntry = (NetEntry *) HashGetValue(hNew);
if (newEntry != 0)
{
NMUndo(newEntry->ne_name, newEntry->ne_prev->ne_name, NMUE_REMOVE);
newEntry->ne_prev->ne_next = newEntry->ne_next;
newEntry->ne_next->ne_prev = newEntry->ne_prev;
}
else
{
/* Create a new entry for this terminal. */
newEntry = (NetEntry *) mallocMagic(sizeof(NetEntry));
newEntry->ne_name = hNew->h_key.h_name;
newEntry->ne_flags = 0;
HashSetValue(hNew, newEntry);
}
newEntry->ne_prev = newEntry;
newEntry->ne_next = newEntry;
/* Now lookup the (supposedly pre-existing) terminal. If it
* doesn't have an entry in the hash table, make a new one.
*/
hOther = HashFind(&nmCurrentNetlist->nl_table, other);
otherEntry = (NetEntry *) HashGetValue(hOther);
if (otherEntry == 0)
{
otherEntry = (NetEntry *) mallocMagic(sizeof(NetEntry));
otherEntry->ne_name = hOther->h_key.h_name;
otherEntry->ne_flags = 0;
otherEntry->ne_prev = otherEntry;
otherEntry->ne_next = otherEntry;
HashSetValue(hOther, otherEntry);
}
/* Tie the new terminal onto other's list. */
if (otherEntry != newEntry)
{
newEntry->ne_prev = otherEntry->ne_prev;
newEntry->ne_next = otherEntry;
newEntry->ne_prev->ne_next = newEntry;
otherEntry->ne_prev = newEntry;
}
NMUndo(new, other, NMUE_ADD);
return otherEntry->ne_name;
}
/*
* ----------------------------------------------------------------------------
*
* NMDeleteTerm --
*
* This procedure removes a terminal from its net.
*
* Results:
* None.
*
* Side effects:
* The named terminal is removed from its net, if it is currently
* in a net.
*
* ----------------------------------------------------------------------------
*/
void
NMDeleteTerm(name)
char *name; /* Name of a terminal. */
{
HashEntry *h;
NetEntry *entry;
if ((name == 0) || (nmCurrentNetlist == NULL)) return;
h = HashLookOnly(&nmCurrentNetlist->nl_table, name);
if (h == NULL) return;
entry = (NetEntry *) HashGetValue(h);
if (entry == 0) return;
nmCurrentNetlist->nl_flags |= NL_MODIFIED;
HashSetValue(h, 0);
NMUndo(entry->ne_name, entry->ne_next->ne_name, NMUE_REMOVE);
entry->ne_next->ne_prev = entry->ne_prev;
entry->ne_prev->ne_next = entry->ne_next;
freeMagic((char *) entry);
}
/*
* ----------------------------------------------------------------------------
*
* NMJoinNets --
*
* This procedure joins two nets together. It is similar to
* NMAddTerm, except that it applies to every terminal in
* the first net rather than just a single terminal.
*
* Results:
* None.
*
* Side effects:
* All of the terminals in the nets associated with termA
* and termB are joined together into a single net.
*
* ----------------------------------------------------------------------------
*/
void
NMJoinNets(termA, termB)
char *termA; /* Name of a terminal in first net. */
char *termB; /* Name of a terminal in second net. */
{
HashEntry *ha, *hb;
NetEntry *netA, *netB, *tmp;
if ((termA == NULL) || (termB == NULL)) return;
if (nmCurrentNetlist == NULL) return;
/* Lookup the two nets, and make sure that they both exist
* and aren't already the same.
*/
ha = HashFind(&nmCurrentNetlist->nl_table, termA);
netA = (NetEntry *) HashGetValue(ha);
hb = HashFind(&nmCurrentNetlist->nl_table, termB);
netB = (NetEntry *) HashGetValue(hb);
if ((netA == 0) || (netB == 0)) return;
nmCurrentNetlist->nl_flags |= NL_MODIFIED;
tmp = netA;
while (TRUE)
{
if (tmp == netB) return;
tmp = tmp->ne_next;
if (tmp == netA) break;
}
/* Record the changes for undo purposes. This code is a bit
* tricky: since termB is used as the reference network for
* deleting all other terminals from it, termB itself must
* be the last terminal to be deleted. Otherwise undo won't
* work.
*/
tmp = netB->ne_next;
while (TRUE)
{
NMUndo(tmp->ne_name, termB, NMUE_REMOVE);
NMUndo(tmp->ne_name, termA, NMUE_ADD);
if (tmp == netB) break;
tmp = tmp->ne_next;
}
/* Join the two nets. */
tmp = netA->ne_prev;
netB->ne_prev->ne_next = netA;
netA->ne_prev = netB->ne_prev;
tmp->ne_next = netB;
netB->ne_prev = tmp;
}
/*
* ----------------------------------------------------------------------------
*
* NMDeleteNet --
*
* This procedure deletes a net by removing all of the terminals
* from it.
*
* Results:
* None.
*
* Side effects:
* All the terminals in the given net are deleted from the net.
*
* ----------------------------------------------------------------------------
*/
void
NMDeleteNet(net)
char *net; /* Name of one of the terminals in the net
* to be deleted.
*/
{
HashEntry *h;
NetEntry *ne, *next;
if ((net == NULL) || (nmCurrentNetlist == NULL)) return;
h = HashLookOnly(&nmCurrentNetlist->nl_table, net);
if (h == NULL) return;
ne = (NetEntry *) HashGetValue(h);
if (ne == 0) return;
nmCurrentNetlist->nl_flags |= NL_MODIFIED;
/* The order of processing is a bit tricky. Since we use net for
* the "current net" in undo-ing, it must be the last terminal
* to be deleted.
*/
next = ne->ne_next;
while (TRUE)
{
NMUndo(next->ne_name, net, NMUE_REMOVE);
HashSetValue(HashFind(&nmCurrentNetlist->nl_table, next->ne_name), 0);
freeMagic((char *) next);
if (next == ne) break;
next = next->ne_next;
}
}
/*
* ----------------------------------------------------------------------------
*
* NMNewNetlist --
*
* This procedure sets everything up to use a new netlist from
* now on. If the netlist isn't already loaded into memory,
* it is read from disk.
*
* Results:
* None.
*
* Side effects:
* A new netlist may be read from disk.
*
* ----------------------------------------------------------------------------
*/
void
NMNewNetlist(name)
char *name; /* Name of the netlist file. If NULL,
* then the netlist file association
* is eliminated.
*/
{
Netlist *new;
FILE *file;
#define MAXLINESIZE 256
char line[MAXLINESIZE], *fullName, *currentTerm, *p;
/* Save undo information, and re-adjust things for the rest
* of this module.
*/
NMUndo(name, NMNetListButton.nmb_text, NMUE_NETLIST);
(void) StrDup(&NMNetListButton.nmb_text, name);
if (NMWindow != NULL)
(void) NMredisplay(NMWindow, &NMNetListButton.nmb_area, (Rect *) NULL);
NMSelectNet((char *) NULL);
if ((name == NULL) || (name[0] == 0))
{
nmCurrentNetlist = NULL;
return;
}
/* First of all, see if this netlist is already loaded. */
for (new = nmListHead; new != NULL; new = new->nl_next)
{
if (strcmp(name, new->nl_name) == 0)
{
nmCurrentNetlist = new;
return;
}
}
/* Create and initialize a new netlist. */
new = (Netlist *) mallocMagic(sizeof(Netlist));
new->nl_name = NULL;
new->nl_fileName = NULL;
HashInit(&new->nl_table, 32, 0);
new->nl_flags = 0;
new->nl_next = nmListHead;
nmListHead = new;
nmCurrentNetlist = new;
(void) StrDup(&new->nl_name, name);
/* Open a file for reading the netlist. If it doesn't exist,
* or doesn't have a proper header line, issue a warning message,
* then just start a new list.
*/
file = PaOpen(name, "r", ".net", Path, CellLibPath, &fullName);
if (file == NULL)
{
TxError("Netlist file %s.net couldn't be found.\n", name);
TxError("Creating new netlist.\n");
new->nl_fileName = mallocMagic((unsigned) (5 + strlen(name)));
(void) sprintf(new->nl_fileName, "%s.net", name);
return;
}
(void) StrDup(&new->nl_fileName, fullName);
if ((fgets(line, MAXLINESIZE, file) == NULL)
|| ((strcasecmp(line, " Net List File\n") != 0) /* Backward compatibility*/
&& (strcasecmp(line, " Netlist File\n") != 0)))
{
TxError("%s isn't a legal netlist file.\n", new->nl_fileName);
TxError("Creating new netlist.\n");
(void) fclose(file);
return;
}
/* Read nets from the file one at a time. Each net consists of
* a bunch of terminal names, one per line. Nets are separated
* by lines that are either empty or have a space as the first
* character. Lines starting with "#" are treated as comments.
* None of this gets recorded for undo-ing.
*/
UndoDisable();
currentTerm = NULL;
while (fgets(line, MAXLINESIZE, file) != NULL)
{
/* Strip the newline character from the end of the line. */
for (p = line; *p != 0; p++)
{
if (*p == '\n')
{
*p = 0;
break;
}
}
if ((line[0] == 0) || (line[0] == ' '))
{
currentTerm = NULL;
continue;
}
if (line[0] == '#') continue;
if (NMTermInList(line) != NULL)
{
TxError("Warning: terminal \"%s\" appears in more than one net.\n",
line);
TxError(" Only the last appearance will be used.\n");
}
if (currentTerm == NULL)
currentTerm = NMAddTerm(line, line);
else (void) NMAddTerm(line, currentTerm);
}
UndoEnable();
nmCurrentNetlist->nl_flags &= ~NL_MODIFIED;
(void) fclose(file);
}
/*
* ----------------------------------------------------------------------------
*
* NMNetlistName --
*
* Return the name of the current net list. Do it as a function to make
* it clear that the exported value is read-only.
*
* Results:
* The name of the current netlist.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
char *
NMNetlistName()
{
if(nmCurrentNetlist!=NULL)
return(nmCurrentNetlist->nl_name);
else
return ((char *) NULL);
}
/*
* ----------------------------------------------------------------------------
*
* NMEnumNets --
*
* This procedure calls a client function for each terminal in
* each net. The supplied function should be of the form:
*
* int
* func(name, firstInNet, clientData)
* char *name;
* bool firstInNet;
* ClientData;
* {
* }
*
* In the above, name is the name of a terminal. FirstInNet
* is TRUE if this is the first terminal in the net, FALSE
* for all other terminals in the net. All the terminals in
* a given net are enumerated consecutively. ClientData is
* an arbitrary parameter value passed in by our caller. Func
* should normally return 0. If it returns a non-zero value,
* the enumeration will be aborted immediately.
*
* Results:
* If the search terminates normally, 0 is returned. If the
* client function returns a non-zero value, then 1 is returned.
*
* Side effects:
* Whatever the client function does.
*
* ----------------------------------------------------------------------------
*/
int NMEnumNets(func, clientData)
int (*func)(); /* Function to call for each terminal. */
ClientData clientData; /* Parameter to pass to function. */
{
HashSearch hs;
HashEntry *h;
NetEntry *entry, *entry2;
int result;
if (nmCurrentNetlist == NULL) return 0;
/* The search runs in two passes. During the first pass, set flags
* to avoid enumerating the same net or terminal twice. During
* the second pass, clear the flags.
*/
HashStartSearch(&hs);
result = 0;
while (TRUE)
{
h = HashNext(&nmCurrentNetlist->nl_table, &hs);
if (h == NULL) break;
entry = (NetEntry *) HashGetValue(h);
if (entry == 0) continue;
if (entry->ne_flags & NETENTRY_SEEN) continue;
entry->ne_flags |= NETENTRY_SEEN;
/* Enumerate this entire net. */
if ((*func)(entry->ne_name, TRUE, clientData) != 0)
{
result = 1;
goto cleanup;
}
for (entry2 = entry->ne_next; entry2 != entry;
entry2 = entry2->ne_next)
{
entry2->ne_flags |= NETENTRY_SEEN;
if ((*func)(entry2->ne_name, FALSE, clientData) != 0)
{
result = 1;
goto cleanup;
}
}
}
cleanup: HashStartSearch(&hs);
while (TRUE)
{
h = HashNext(&nmCurrentNetlist->nl_table, &hs);
if (h == NULL) break;
entry = (NetEntry *) HashGetValue(h);
if (entry != 0) entry->ne_flags &= ~NETENTRY_SEEN;
}
return result;
}
/*
* ----------------------------------------------------------------------------
*
* NMEnumTerms --
*
* This procedure calls a client function for each terminal in
* a given net. The supplied function should be of the form:
*
* int
* func(name, clientData)
* char *name;
* ClientData;
* {
* }
*
* In the above, name is the name of a terminal. The terminals
* in a given net are enumerated consecutively. ClientData is
* an arbitrary parameter value passed in by our caller. The
* client function should return 0 under normal conditions. If
* it wishes to abort the search, it should return 1.
*
* Results:
* If the search terminates normally, 0 is returned. If the
* client function returns a non-zero value, then 1 is returned.
*
* Side effects:
* Whatever the client function does.
*
* ----------------------------------------------------------------------------
*/
int
NMEnumTerms(name, func, clientData)
char *name; /* Name of terminal in net to be enumerated. */
int (*func)(); /* Function to call for each terminal. */
ClientData clientData; /* Parameter to pass to function. */
{
HashEntry *h;
NetEntry *entry, *entry2;
if (nmCurrentNetlist == NULL) return 0;
h = HashLookOnly(&nmCurrentNetlist->nl_table, name);
if (h == NULL) return 0;
entry = (NetEntry *) HashGetValue(h);
if (entry == NULL) return 0;
entry2 = entry;
while (TRUE)
{
if ((*func)(entry2->ne_name, clientData) != 0) return 1;
entry2 = entry2->ne_next;
if (entry2 == entry) break;
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* NMHasList --
*
* This procedure checks to see if a netlist is selected. It is used
* to let the outside world know.
*
* Results:
* TRUE if the netlist is selected. Otherwise FALSE.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
bool
NMHasList()
{
return(nmCurrentNetlist != NULL);
}
/*
* ----------------------------------------------------------------------------
*
* NMTermInList --
*
* Tells whether or not the given terminal name is in the current
* netlist.
*
* Results:
* If the terminal isn't part of any net, NULL is returned.
* If it is part of some net, the terminal's name from the
* hash table is returned. This is a token that won't go
* away, which the caller can use to remember the net name.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
char *
NMTermInList(name)
char *name; /* Name of terminal. */
{
HashEntry *h;
NetEntry *entry;
if (nmCurrentNetlist == NULL) return NULL;
h = HashLookOnly(&nmCurrentNetlist->nl_table, name);
if (h == NULL) return NULL;
entry = (NetEntry *) HashGetValue(h);
if (entry == NULL) return NULL;
return entry->ne_name;
}
/*
* ----------------------------------------------------------------------------
*
* NMWriteNetlist --
*
* This procedure writes out a netlist to a file.
*
* Results:
* None.
*
* Side effects:
* The file on disk is overwritten.
*
* ----------------------------------------------------------------------------
*/
void
NMWriteNetlist(fileName)
char *fileName; /* If non-NULL, gives name of file in
* which to write current netlist. If NULL,
* the netlist gets written to the place
* from which it was read.
*/
{
FILE *file;
int nmWriteNetsFunc();
char *realName, line[50];
char *alloc = NULL;
if (nmCurrentNetlist == NULL)
{
TxError("There isn't a current net list to write.\n");
return;
}
/* Decide what file to use to write the file (if an explicit name
* is given, we have to add on a ".net" extension, and we also
* check to make sure the file doesn't exist).
*/
if (fileName == NULL) realName = nmCurrentNetlist->nl_fileName;
else
{
realName = alloc = mallocMagic((unsigned) (5 + strlen(fileName)));
(void) sprintf(realName, "%s.net", fileName);
file = PaOpen(realName, "r", (char *) NULL, ".",
(char *) NULL, (char **) NULL);
if (file != NULL)
{
(void) fclose(file);
TxPrintf("Net list file %s already exists.", realName);
TxPrintf(" Should I overwrite it? [no] ");
if (TxGetLine(line, sizeof(line)) == (char *) NULL)
{
freeMagic(alloc);
return;
}
if ((strcmp(line, "y") != 0) && (strcmp(line, "yes") != 0))
{
freeMagic(alloc);
return;
}
}
}
file = PaOpen(realName, "w", (char *) NULL, ".",
(char *) NULL, (char **) NULL);
if (file == NULL)
{
TxError("Couldn't write file %s.\n", realName);
if (alloc) freeMagic(alloc);
return;
}
fprintf(file, " Netlist File\n");
(void) NMEnumNets(nmWriteNetsFunc, (ClientData) file);
if (strcmp(realName, nmCurrentNetlist->nl_fileName) == 0)
nmCurrentNetlist->nl_flags &= ~NL_MODIFIED;
(void) fclose(file);
if (alloc) freeMagic(alloc);
}
int
nmWriteNetsFunc(name, firstInNet, file)
char *name; /* Name of terminal. */
bool firstInNet; /* TRUE means first terminal in net. */
FILE *file; /* File in which to write info. */
{
if (firstInNet) fputs("\n", file);
fprintf(file, "%s\n", name);
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* NMCheckWritten --
*
* Checks to see if there are netlists that have been modified
* but not written back to disk. If so, asks user whether he
* cares about them.
*
* Results:
* Returns TRUE if there are no modified netlists around, or
* if the user says he doesn't care about them. Returns FALSE
* if the user says he cares.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
bool
NMCheckWritten()
{
char *name;
Netlist *nl;
int count, indx;
char answer[12];
count = 0;
for (nl = nmListHead; nl != NULL; nl = nl->nl_next)
{
if (nl->nl_flags & NL_MODIFIED)
{
count += 1;
name = nl->nl_name;
}
}
if (count == 0) return TRUE;
do
{
if (count == 1)
TxPrintf("Net-list \"%s\" has been modified.", name);
else
TxPrintf("%d netlists have been modified.", count);
TxPrintf(" Do you want to lose the changes? [no] ");
if ((TxGetLine(answer, sizeof answer) == NULL) || (answer[0] == 0))
return FALSE;
indx = Lookup(answer, yesno);
} while (indx < 0);
return indx;
}
/*
* ----------------------------------------------------------------------------
*
* NMWriteAll --
*
* Goes through all netlists that have been modified, asking
* the user whether to write out the netlist or not.
*
* Results:
* None.
*
* Side effects:
* Net-lists may be written to disk.
*
* ----------------------------------------------------------------------------
*/
void
NMWriteAll()
{
Netlist *nl, *saveCurrent;
static const char * const options[] = {"write", "skip", "abort", NULL};
char answer[10];
int indx;
saveCurrent = nmCurrentNetlist;
for (nl = nmListHead; nl != NULL; nl = nl->nl_next)
{
if ((nl->nl_flags & NL_MODIFIED) == 0) continue;
do
{
TxPrintf("%s: write, skip, or abort command? [write] ",
nl->nl_name);
if (TxGetLine(answer, sizeof answer) == NULL) continue;
if (answer[0] == 0) indx = 0;
else indx = Lookup(answer, options);
} while (indx < 0);
switch (indx)
{
case 0:
nmCurrentNetlist = nl;
NMWriteNetlist((char *) NULL);
break;
case 1:
break;
case 2:
return;
}
}
nmCurrentNetlist = saveCurrent;
return;
}
/*
* ----------------------------------------------------------------------------
*
* NMFlushNetlist --
*
* This procedure flushes the contents of the named netlist
* from memory. If the netlist was modified, the user is given
* a chance to abort the flush.
*
* Results:
* None.
*
* Side effects:
* The contents of the netlist are changed. If the netlist
* had been modified, all previous undo events are flushed.
*
* ----------------------------------------------------------------------------
*/
void
NMFlushNetlist(name)
char *name; /* Name of the netlist to be flushed. */
{
Netlist *list, **prev;
HashSearch hs;
HashEntry *h;
/* Find the netlist in question. */
list = NULL;
for (prev = &nmListHead; *prev != NULL; prev = &(*prev)->nl_next)
{
if (strcmp(name, (*prev)->nl_name) == 0)
{
list = *prev;
break;
}
}
if (list == NULL)
{
TxError("Netlist \"%s\" isn't currently loaded.\n", name);
return;
}
/* If the netlist has been modified, give the user a chance to
* skip this.
*/
if (list->nl_flags & NL_MODIFIED)
{
char answer[10];
int indx;
while (TRUE)
{
TxPrintf("Really throw away all changes made ");
TxPrintf("to netlist \"%s\"? [no] ", name);
if ((TxGetLine(answer, sizeof answer) == NULL) || (answer[0] == 0))
return;
indx = Lookup(answer, yesno);
if (indx == 0) return;
if (indx == 1)
{
UndoFlush();
break;
}
}
}
/* Unlink the netlist from the list of netlists, and free up
* everything in it.
*/
*prev = list->nl_next;
HashStartSearch(&hs);
while (TRUE)
{
h = HashNext(&list->nl_table, &hs);
if (h == NULL) break;
if (HashGetValue(h) != NULL)
freeMagic((char *) HashGetValue(h));
}
freeMagic((char *) list);
/* If the netlist was the current netlist, read it in again from
* disk.
*/
if (list == nmCurrentNetlist)
NMNewNetlist(name);
}