1 /*
2  * ss_cazm.c: CAZM- and ASCII- format routines for SpiceStream
3  *
4  * Copyright (C) 1998,1999  Stephen G. Tell
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public
17  * License along with this software; if not, write to the Free
18  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  *
21  * CAzM and "ascii" format are closely related, so they are both handled
22  * in this file.
23  *
24  * CAzM format is intended to handles files written by MCNC's CAzM simulator,
25  * used by a number of universities, and its commercial decendant,
26  * the TSPICE product from Tanner Research.
27  *
28  * CAzM-format files contain a multiline header.  The second to last line
29  * of the header identifies the analysis type, for example TRANSIENT or AC.
30  * The last line of the header contains the names of the variables, seperated
31  * by whitespace.
32  *
33  * Ascii-format files have a one-line header, containing a space- or
34  * tab-speperated list of the variable names.  To avoid treating a file
35  * containing random binary garbage as an ascii-format file, we require the
36  * header line to contain space, tab, and USASCII printable characters only.
37  *
38  */
39 #define _FILE_OFFSET_BITS 64
40 
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <ctype.h>
45 #include <float.h>
46 
47 #include <config.h>
48 #include <glib.h>
49 #include "spicestream.h"
50 
51 static int sf_readrow_ascii(SpiceStream *sf, double *ivar, double *dvars);
52 static SpiceStream *ascii_process_header(char *line, VarType ivtype,
53 				  char *fname, int lineno);
54 
55 /* Read spice-type file header - cazm format */
56 SpiceStream *
sf_rdhdr_cazm(char * name,FILE * fp)57 sf_rdhdr_cazm(char *name, FILE *fp)
58 {
59 	SpiceStream *sf;
60 	char *line = NULL;
61 	int lineno = 0;
62 	int linesize = 1024;
63 	int done = 0;
64 	VarType ivtype;
65 
66 	while(!done) {
67 		if((fread_line(fp, &line, &linesize) == EOF) || lineno > 30) {
68 			g_free(line);
69 			return NULL;
70 		}
71 		lineno++;
72 		/* "section header" line */
73 		if(strncmp(line, "TRANSIENT", 9) == 0) {
74 			ivtype = TIME;
75 			done = 1;
76 		} else if(strncmp(line, "AC ANALYSIS", 11) == 0) {
77 			ivtype = FREQUENCY;
78 			done = 1;
79 		} else if(strncmp(line, "TRANSFER", 8) == 0) {
80 			/* DC transfer function - ivar might also be current,
81 			 * but we can't tell */
82 			ivtype = VOLTAGE;
83 			done = 1;
84 		}
85 	}
86 
87 	/* line after header contains signal names
88 	 * first one is assumed to be the independent variable.
89 	 */
90 	if(fread_line(fp, &line, &linesize) == EOF) {
91 		g_free(line);
92 		return NULL;
93 	}
94 	lineno++;
95 
96 	sf = ascii_process_header(line, ivtype, name, lineno);
97 	if(!sf)
98 		goto fail;
99 
100 	sf->fp = fp;
101 	sf->lineno = lineno;
102 	sf->linebuf = line;
103 	sf->lbufsize = linesize;
104 	return sf;
105 
106  fail:
107 	if(line)
108 		g_free(line);
109 	return NULL;
110 }
111 
112 
113 /* Read spice-type file header - ascii format */
114 SpiceStream *
sf_rdhdr_ascii(char * name,FILE * fp)115 sf_rdhdr_ascii(char *name, FILE *fp)
116 {
117 	SpiceStream *sf;
118 	char *line = NULL;
119 	int lineno = 0;
120 	int linesize = 1024;
121 	char *cp;
122 
123 	/*
124 	 * first line is expected to contain space-seperated
125 	 * variable names.
126 	 * first one is assumed to be the independent variable.
127 	 */
128 	if(fread_line(fp, &line, &linesize) == EOF) {
129 		goto fail;
130 	}
131 	lineno++;
132 
133 	/* Check for non-ascii characters in header, to reject
134 	 * binary files.
135 	 */
136 	for(cp = line; *cp; cp++) {
137 		if(!isgraph(*cp) && *cp != ' ' && *cp != '\t') {
138 			goto fail;
139 		}
140 	}
141 
142 	sf = ascii_process_header(line, UNKNOWN, name, lineno);
143 	if(!sf)
144 		goto fail;
145 
146 	sf->fp = fp;
147 	sf->lineno = lineno;
148 	sf->linebuf = line;
149 	sf->lbufsize = linesize;
150 	return sf;
151 
152  fail:
153 	if(line)
154 		g_free(line);
155 	return NULL;
156 }
157 
158 
159 /*
160  * Process a header line from an ascii or cazm format file.
161  * Returns a filled-in SpiceStream* with variable information.
162  */
163 static
ascii_process_header(char * line,VarType ivtype,char * fname,int lineno)164 SpiceStream *ascii_process_header(char *line, VarType ivtype,
165 				  char *fname, int lineno)
166 {
167 	SpiceStream *sf;
168 	char *signam;
169 	int dvsize = 64;
170 
171 	signam = strtok(line, " \t\n");
172 	if(!signam) {
173 		ss_msg(ERR, "ascii_process_header", "%s:%d: syntax error in header", fname, lineno);
174 		return NULL;
175 	}
176 
177 	/* a bit of a hack: get ss_new to allocate additional
178 	 * dvars, then only use the entries we need or allocate more
179 	 */
180 	sf = ss_new(NULL, fname, dvsize, 0);
181 
182 	if(ivtype == UNKNOWN) {
183 		if(strcasecmp(signam, "time") == 0)
184                        sf->ivar->type = TIME;
185 	} else {
186                sf->ivar->type = ivtype;
187 	}
188 	sf->ivar->name = g_strdup(signam);
189 	sf->ivar->col = 0;
190 	sf->ivar->ncols = 1;
191 
192 	sf->ndv = 0;
193 	sf->ncols = 1;
194 	sf->ntables = 1;
195 	while((signam = strtok(NULL, " \t\n")) != NULL) {
196 		if(sf->ndv >= dvsize) {
197 			dvsize *= 2;
198 			sf->dvar = g_realloc(sf->dvar, dvsize * sizeof(SpiceVar));
199 		}
200 		sf->dvar[sf->ndv].name = g_strdup(signam);
201 		sf->dvar[sf->ndv].type = UNKNOWN;
202 		sf->dvar[sf->ndv].col = sf->ncols;
203 		sf->dvar[sf->ndv].ncols = 1;
204 		sf->ndv++;
205 		sf->ncols++;
206 	}
207 	sf->readrow = sf_readrow_ascii;
208 
209 	return sf;
210 }
211 
212 
213 
214 /* Read row of values from ascii- or cazm- format file.
215  * Possibly reusable for other future formats with lines of
216  * whitespace-seperated values.
217  * Returns:
218  *	1 on success.  also fills in *ivar scalar and *dvars vector
219  *	0 on EOF
220  *	-1 on error  (may change some ivar/dvar values)
221  */
222 static int
sf_readrow_ascii(SpiceStream * sf,double * ivar,double * dvars)223 sf_readrow_ascii(SpiceStream *sf, double *ivar, double *dvars)
224 {
225 	int i;
226 	char *tok;
227 
228 	if(fread_line(sf->fp, &sf->linebuf, &sf->lbufsize) == EOF) {
229 		return 0;
230 	}
231 	sf->lineno++;
232 
233 	tok = strtok(sf->linebuf, " \t\n");
234 	if(!tok) {
235 		return 0;  /* blank line can indicate end of data */
236 	}
237 
238 	/* check to see if it is numeric: ascii format is so loosly defined
239 	 * that we might read a load of garbage otherwise. */
240 
241 	if(strspn(tok, "0123456789eE+-.") != strlen(tok)) {
242 		ss_msg(ERR, "sf_readrow_ascii", "%s:%d: expected number; maybe this isn't an ascii data file at all?", sf->filename, sf->lineno, i);
243 		return -1;
244 	}
245 
246 	*ivar = atof(tok);
247 
248 	for(i = 0; i < sf->ncols-1; i++) {
249 		tok = strtok(NULL, " \t\n");
250 		if(!tok) {
251 			ss_msg(ERR, "sf_readrow_ascii", "%s:%d: data field %d missing", sf->filename, sf->lineno, i);
252 			return -1;
253 		}
254 		dvars[i] = atof(tok);
255 	}
256 	return 1;
257 }
258