You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
500 lines
16 KiB
500 lines
16 KiB
/**********
|
|
Copyright 1990 Regents of the University of California. All rights reserved.
|
|
Author: 1985 Wayne A. Christopher
|
|
**********/
|
|
|
|
/*
|
|
* For dealing with spice input decks and command scripts
|
|
*/
|
|
|
|
/*
|
|
* SJB 21 April 2005
|
|
* Added support for end-of-line comments that begin with any of the following:
|
|
* ';' (for PSpice compatability)
|
|
* '$' (for HSpice compatability)
|
|
* '//' (like in c++ and as per the numparam code)
|
|
* '--' (as per the numparam code)
|
|
* Any following text to the end of the line is ignored.
|
|
* Comments on a contunuation line (i.e. line begining with '+') are allowed
|
|
* and are removed before lines are stitched.
|
|
* Lines that contain only an end-of-line comment with or withou leading white
|
|
* space are also allowed.
|
|
*/
|
|
|
|
/*
|
|
* SJB 22 May 2001
|
|
* Fixed memory leaks in inp_readall() when first(?) line of input begins with a '@'.
|
|
* Fixed memory leaks in inp_readall() when .include lines have errors
|
|
* Fixed crash where a NULL pointer gets freed in inp_readall()
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include "ngspice.h"
|
|
#include "cpdefs.h"
|
|
#include "ftedefs.h"
|
|
#include "dvec.h"
|
|
#include "fteinp.h"
|
|
|
|
|
|
#include "inpcom.h"
|
|
#include "variable.h"
|
|
|
|
#ifdef XSPICE
|
|
/* gtri - add - 12/12/90 - wbk - include new stuff */
|
|
#include "ipctiein.h"
|
|
#include "enh.h"
|
|
/* gtri - end - 12/12/90 */
|
|
#endif
|
|
|
|
/* SJB - Uncomment this line for debug tracing */
|
|
/*#define TRACE */
|
|
|
|
/* static declarations */
|
|
static char * readline(FILE *fd);
|
|
static void inp_stripcomments_deck(struct line *deck);
|
|
static void inp_stripcomments_line(char * s);
|
|
|
|
/*-------------------------------------------------------------------------*
|
|
* This routine reads a line (of arbitrary length), up to a '\n' or 'EOF' *
|
|
* and returns a pointer to the resulting null terminated string. *
|
|
* The '\n' if found, is included in the returned string. *
|
|
* From: jason@ucbopal.BERKELEY.EDU (Jason Venner) *
|
|
* Newsgroups: net.sources *
|
|
*-------------------------------------------------------------------------*/
|
|
#define STRGROW 256
|
|
|
|
static char *
|
|
readline(FILE *fd)
|
|
{
|
|
int c;
|
|
int memlen;
|
|
char *strptr;
|
|
int strlen;
|
|
|
|
strptr = NULL;
|
|
strlen = 0;
|
|
memlen = STRGROW;
|
|
strptr = tmalloc(memlen);
|
|
memlen -= 1; /* Save constant -1's in while loop */
|
|
while((c = getc(fd)) != EOF) {
|
|
if (strlen == 0 && (c == '\n' || c == ' ')) /* Leading spaces away */
|
|
continue;
|
|
strptr[strlen] = c;
|
|
strlen++;
|
|
if( strlen >= memlen ) {
|
|
memlen += STRGROW;
|
|
if( !(strptr = trealloc(strptr, memlen + 1))) {
|
|
return (NULL);
|
|
}
|
|
}
|
|
if (c == '\n') {
|
|
break;
|
|
}
|
|
}
|
|
if (!strlen) {
|
|
tfree(strptr);
|
|
return (NULL);
|
|
}
|
|
strptr[strlen] = '\0';
|
|
/* Trim the string */
|
|
strptr = trealloc(strptr, strlen + 1);
|
|
return (strptr);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------*
|
|
* Look up the variable sourcepath and try everything in the list in order *
|
|
* if the file isn't in . and it isn't an abs path name. *
|
|
*-------------------------------------------------------------------------*/
|
|
FILE *
|
|
inp_pathopen(char *name, char *mode)
|
|
{
|
|
FILE *fp;
|
|
char buf[BSIZE_SP];
|
|
struct variable *v;
|
|
|
|
/* If this is an abs pathname, or there is no sourcepath var, just
|
|
* do an fopen.
|
|
*/
|
|
if (index(name, DIR_TERM)
|
|
|| !cp_getvar("sourcepath", VT_LIST, (char *) &v))
|
|
return (fopen(name, mode));
|
|
|
|
while (v) {
|
|
switch (v->va_type) {
|
|
case VT_STRING:
|
|
cp_wstrip(v->va_string);
|
|
(void) sprintf(buf, "%s%s%s", v->va_string, DIR_PATHSEP, name);
|
|
break;
|
|
case VT_NUM:
|
|
(void) sprintf(buf, "%d%s%s", v->va_num, DIR_PATHSEP, name);
|
|
break;
|
|
case VT_REAL: /* This is foolish */
|
|
(void) sprintf(buf, "%g%s%s", v->va_real, DIR_PATHSEP, name);
|
|
break;
|
|
}
|
|
if ((fp = fopen(buf, mode)))
|
|
return (fp);
|
|
v = v->va_next;
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Read the entire input file and return a pointer to the first line of
|
|
* the linked list of 'card' records in data. The pointer is stored in
|
|
* *data.
|
|
*-------------------------------------------------------------------------*/
|
|
void
|
|
inp_readall(FILE *fp, struct line **data)
|
|
{
|
|
struct line *end = NULL, *cc = NULL, *prev = NULL, *working, *newcard;
|
|
char *buffer, *s, *t, c;
|
|
/* segfault fix */
|
|
char *copys=NULL;
|
|
int line_number = 1; /* sjb - renamed to avoid confusion with struct line */
|
|
FILE *newfp;
|
|
|
|
/* Must set this to NULL or non-tilde includes segfault. -- Tim Molteno */
|
|
/* copys = NULL; */ /* This caused a parse error with gcc 2.96. Why??? */
|
|
|
|
/* gtri - modify - 12/12/90 - wbk - read from mailbox if ipc enabled */
|
|
#ifdef XSPICE
|
|
Ipc_Status_t ipc_status;
|
|
char ipc_buffer[1025]; /* Had better be big enough */
|
|
int ipc_len;
|
|
|
|
/* First read in all lines & put them in the struct cc */
|
|
while (1) {
|
|
|
|
/* If IPC is not enabled, do equivalent of what SPICE did before */
|
|
if(! g_ipc.enabled) {
|
|
buffer = readline(fp);
|
|
if(! buffer)
|
|
break;
|
|
}
|
|
else {
|
|
/* else, get the line from the ipc channel. */
|
|
/* We assume that newlines are not sent by the client */
|
|
/* so we add them here */
|
|
ipc_status = ipc_get_line(ipc_buffer, &ipc_len, IPC_WAIT);
|
|
if(ipc_status == IPC_STATUS_END_OF_DECK) {
|
|
buffer = NULL;
|
|
break;
|
|
}
|
|
else if(ipc_status == IPC_STATUS_OK) {
|
|
buffer = (void *) MALLOC(strlen(ipc_buffer) + 3);
|
|
strcpy(buffer, ipc_buffer);
|
|
strcat(buffer, "\n");
|
|
}
|
|
else { /* No good way to report this so just die */
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/* gtri - end - 12/12/90 */
|
|
#else
|
|
while ((buffer = readline(fp))) {
|
|
#endif
|
|
|
|
#ifdef TRACE
|
|
/* SDB debug statement */
|
|
printf ("in inp_readall, just read '%s' . . .\n", buffer);
|
|
#endif
|
|
|
|
/* OK -- now we have loaded the next line into 'buffer'. Process it. */
|
|
/* If input line is blank, ignore it & continue looping. */
|
|
if ( (strcmp(buffer,"\n") == 0)
|
|
|| (strcmp(buffer,"\r\n") == 0) ) {
|
|
continue;
|
|
}
|
|
|
|
|
|
if (*buffer == '@') {
|
|
tfree(buffer); /* was allocated by readline() */
|
|
break;
|
|
}
|
|
|
|
|
|
/* loop through 'buffer' until end is reached. Then test for
|
|
premature end. If premature end is reached, spew
|
|
error and zap the line. */
|
|
for (s = buffer; *s && (*s != '\n'); s++);
|
|
if (!*s) {
|
|
fprintf(cp_err, "Warning: premature EOF\n");
|
|
}
|
|
*s = '\0'; /* Zap the newline. */
|
|
|
|
if(*(s-1) == '\r') /* Zop the carriage return under windows */
|
|
*(s-1) = '\0';
|
|
|
|
/* now handle .include statements */
|
|
if (ciprefix(".include", buffer)) {
|
|
for (s = buffer; *s && !isspace(*s); s++) /* advance past non-space chars */
|
|
;
|
|
while (isspace(*s)) /* now advance past space chars */
|
|
s++;
|
|
if (!*s) { /* if at end of line, error */
|
|
fprintf(cp_err, "Error: .include filename missing\n");
|
|
tfree(buffer); /* was allocated by readline() */
|
|
continue;
|
|
} /* Now s points to first char after .include */
|
|
for (t = s; *t && !isspace(*t); t++) /* now advance past non-space chars */
|
|
;
|
|
*t = '\0'; /* place \0 and end of file name in buffer */
|
|
|
|
if (*s == '~') {
|
|
copys = cp_tildexpand(s); /* allocates memory, but can also return NULL */
|
|
if(copys != NULL) {
|
|
s = copys; /* reuse s, but remember, buffer still points to allocated memory */
|
|
}
|
|
}
|
|
|
|
/* open file specified by .include statement */
|
|
if (!(newfp = inp_pathopen(s, "r"))) {
|
|
perror(s);
|
|
if(copys) {
|
|
tfree(copys); /* allocated by the cp_tildexpand() above */
|
|
}
|
|
tfree(buffer); /* allocated by readline() above */
|
|
continue;
|
|
}
|
|
|
|
if(copys) {
|
|
tfree(copys); /* allocated by the cp_tildexpand() above */
|
|
}
|
|
|
|
inp_readall(newfp, &newcard); /* read stuff in include file into netlist */
|
|
(void) fclose(newfp);
|
|
|
|
/* Make the .include a comment */
|
|
*buffer = '*';
|
|
|
|
/* now check if this is the first pass (i.e. end points to null) */
|
|
if (end) { /* end already exists */
|
|
end->li_next = alloc(struct line); /* create next card */
|
|
end = end->li_next; /* make end point to next card */
|
|
} else {
|
|
end = cc = alloc(struct line); /* create the deck & end. cc will
|
|
point to beginning of deck, end to
|
|
the end */
|
|
}
|
|
|
|
/* now fill out rest of struct end. */
|
|
end->li_next = NULL;
|
|
end->li_error = NULL;
|
|
end->li_actual = NULL;
|
|
end->li_line = copy(buffer);
|
|
end->li_linenum = line_number++;
|
|
end->li_next = newcard;
|
|
|
|
/* Renumber the lines */
|
|
for (end = newcard; end && end->li_next; end = end->li_next)
|
|
end->li_linenum = line_number++;
|
|
end->li_linenum = line_number++; /* SJB - renumber the last line */
|
|
|
|
/* Fix the buffer up a bit. */
|
|
(void) strncpy(buffer + 1, "end of:", 7);
|
|
} /* end of .include handling */
|
|
|
|
/* now check if this is the first pass (i.e. end points to null) */
|
|
if (end) { /* end already exists */
|
|
end->li_next = alloc(struct line); /* create next card */
|
|
end = end->li_next; /* point to next card */
|
|
} else { /* End doesn't exist. Create it. */
|
|
end = cc = alloc(struct line); /* note that cc points to beginning
|
|
of deck, end to the end */
|
|
}
|
|
|
|
/* now put buffer into li */
|
|
end->li_next = NULL;
|
|
end->li_error = NULL;
|
|
end->li_actual = NULL;
|
|
end->li_line = buffer;
|
|
end->li_linenum = line_number++;
|
|
}
|
|
|
|
if (!end) { /* No stuff here */
|
|
*data = NULL;
|
|
return;
|
|
} /* end while ((buffer = readline(fp))) */
|
|
|
|
/* This should be freed because we are done with it. */
|
|
/* tfree(buffer); */
|
|
|
|
|
|
/* Now clean up li: remove comments & stitch together continuation lines. */
|
|
working = cc->li_next; /* cc points to head of deck. Start with the
|
|
next card. */
|
|
|
|
/* sjb - strip or convert end-of-line comments.
|
|
This must be cone before stitching continuation lines.
|
|
If the line only contains an end-of-line comment then it is converted
|
|
into a normal comment with a '*' at the start. This will then get
|
|
stripped in the following code. */
|
|
inp_stripcomments_deck(working);
|
|
|
|
while (working) {
|
|
for (s = working->li_line; (c = *s) && c <= ' '; s++)
|
|
;
|
|
|
|
#ifdef TRACE
|
|
/* SDB debug statement */
|
|
printf("In inp_readall, processing linked list element line = %d, s = %s . . . \n", working->li_linenum,s);
|
|
#endif
|
|
|
|
switch (c) {
|
|
case '#':
|
|
case '$':
|
|
case '*':
|
|
case '\0':
|
|
/* this used to be commented out. Why? */
|
|
/* prev = NULL; */
|
|
working = working->li_next; /* for these chars, go to next card */
|
|
break;
|
|
|
|
case '+': /* handle continuation */
|
|
if (!prev) {
|
|
working->li_error = copy(
|
|
"Illegal continuation line: ignored.");
|
|
working = working->li_next;
|
|
break;
|
|
}
|
|
|
|
/* create buffer and write last and current line into it. */
|
|
buffer = tmalloc(strlen(prev->li_line) + strlen(s) + 2);
|
|
(void) sprintf(buffer, "%s %s", prev->li_line, s + 1);
|
|
|
|
s = prev->li_line;
|
|
prev->li_line = buffer;
|
|
prev->li_next = working->li_next;
|
|
working->li_next = NULL;
|
|
if (prev->li_actual) {
|
|
for (end = prev->li_actual;
|
|
end->li_next; end = end->li_next)
|
|
;
|
|
end->li_next = working;
|
|
tfree(s);
|
|
} else {
|
|
newcard = alloc(struct line);
|
|
newcard->li_linenum = prev->li_linenum;
|
|
newcard->li_line = s;
|
|
newcard->li_next = working;
|
|
newcard->li_error = NULL;
|
|
newcard->li_actual = NULL;
|
|
prev->li_actual = newcard;
|
|
}
|
|
working = prev->li_next;
|
|
break;
|
|
|
|
default: /* regular one-line card */
|
|
prev = working;
|
|
working = working->li_next;
|
|
break;
|
|
}
|
|
}
|
|
|
|
*data = cc;
|
|
return;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*
|
|
* *
|
|
*-------------------------------------------------------------------------*/
|
|
void
|
|
inp_casefix(char *string)
|
|
{
|
|
#ifdef HAVE_CTYPE_H
|
|
if (string)
|
|
while (*string) {
|
|
/* Let's make this really idiot-proof. */
|
|
#ifdef HAS_ASCII
|
|
*string = strip(*string);
|
|
#endif
|
|
if (*string == '"') {
|
|
*string++ = ' ';
|
|
while (*string && *string != '"')
|
|
string++;
|
|
if (*string == '"')
|
|
*string = ' ';
|
|
}
|
|
if (!isspace(*string) && !isprint(*string))
|
|
*string = '_';
|
|
if (isupper(*string))
|
|
*string = tolower(*string);
|
|
string++;
|
|
}
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
|
|
/* Strip all end-of-line comments from a deck */
|
|
static void
|
|
inp_stripcomments_deck(struct line *deck)
|
|
{
|
|
struct line *c=deck;
|
|
while( c!=NULL) {
|
|
inp_stripcomments_line(c->li_line);
|
|
c= c->li_next;
|
|
}
|
|
}
|
|
|
|
/* Strip end of line comment from a string and remove trailing white space
|
|
supports comments that begin with single characters ';' or '$'
|
|
or double characters '//' or '--'
|
|
If there is only white space before the end-of-line comment the
|
|
the whole line is converted to a normal comment line (i.e. one that
|
|
begins with a '*').
|
|
BUG: comment characters in side of string literals are not ignored. */
|
|
static void
|
|
inp_stripcomments_line(char * s)
|
|
{
|
|
char c = ' '; /* anything other than a comment character */
|
|
char * d = s;
|
|
if(*s=='\0') return; /* empty line */
|
|
|
|
/* look for comment */
|
|
while((c=*d)!='\0') {
|
|
d++;
|
|
if( (*d==';') || (*d=='$') ) {
|
|
break;
|
|
} else if( (*d==c) && ((c=='/') || (c=='-'))) {
|
|
*d--; /* move d back to first comment character */
|
|
break;
|
|
}
|
|
}
|
|
/* d now points to the first comment character of the null at the string end */
|
|
|
|
/* check for special case of comment at start of line */
|
|
if(d==s) {
|
|
*s = '*'; /* turn into normal comment */
|
|
return;
|
|
}
|
|
|
|
if(d>s) {
|
|
d--;
|
|
/* d now points to character just before comment */
|
|
|
|
/* eat white space at end of line */
|
|
while(d>=s) {
|
|
if( (*d!=' ') && (*d!='\t' ) )
|
|
break;
|
|
d--;
|
|
}
|
|
d++;
|
|
/* d now points to the first white space character before the
|
|
end-of-line or end-of-line comment, or it points to the first
|
|
end-of-line comment character, or to the begining of the line */
|
|
}
|
|
|
|
/* Check for special case of comment at start of line
|
|
with or without preceeding white space */
|
|
if(d<=s) {
|
|
*s = '*'; /* turn the whole line into normal comment */
|
|
return;
|
|
}
|
|
|
|
*d='\0'; /* terminate line in new location */
|
|
}
|