1 /*
2  * ss_spice3.c: routines for SpiceStream that handle the file formats
3  * 	known as Berkeley Spice3 Rawfile
4  *
5  * Copyright (C) 1998,1999  Stephen G. Tell
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the Free
19  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  */
22 
23 #include "ssintern.h"
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <ctype.h>
29 #include <float.h>
30 
31 #include <config.h>
32 #include <glib.h>
33 #include "spicestream.h"
34 
35 static int sf_readrow_s3raw(SpiceStream *sf, double *ivar, double *dvars);
36 char *msgid = "s3raw";
37 static int sf_readrow_s3bin(SpiceStream *sf, double *ivar, double *dvars);
38 
39 /* convert variable type string from spice3 raw file to
40  * our type numbers
41  */
42 static VarType
sf_str2type_s3raw(char * s)43 sf_str2type_s3raw(char *s)
44 {
45 	if(strcasecmp(s, "voltage") == 0)
46 		return VOLTAGE;
47 	else if(strcasecmp(s, "current") == 0)
48 		return CURRENT;
49 	else if(strcasecmp(s, "frequency") == 0)
50 		return FREQUENCY;
51 	else if(strcasecmp(s, "time") == 0)
52 		return TIME;
53 	else return UNKNOWN;
54 }
55 
56 
57 /* Read spice-type file header - Berkeley Spice3 "raw" format */
58 SpiceStream *
sf_rdhdr_s3raw(char * name,FILE * fp)59 sf_rdhdr_s3raw(char *name, FILE *fp)
60 {
61 	SpiceStream *sf = NULL;
62 	char *line = NULL;
63 	int lineno = 0;
64 	int linesize = 1024;
65 	char *key, *val;
66 	int nvars, npoints;
67 	int got_nvars = 0;
68 	int got_values = 0;
69 	int dtype_complex = 0;
70 	int binary = 0;
71 	char *vnum, *vname, *vtypestr;
72 	int i;
73 
74 	while(fread_line(fp, &line, &linesize) != EOF) {
75 		lineno++;
76 		if(lineno == 1 && strncmp(line, "Title: ", 7)) {
77 			/* not a spice3raw file; bail out */
78 			ss_msg(DBG, msgid, "%s:%d: Doesn't look like a spice3raw file; \"Title:\" expected\n", name, lineno);
79 
80 			return NULL;
81 		}
82 
83 		key = strtok(line, ":");
84 		if(!key) {
85 			ss_msg(ERR, msgid, "%s:%d: syntax error, expected \"keyword:\"", name, lineno);
86 			g_free(line);
87 			return NULL;
88 		}
89 		if(strcmp(key, "Flags") == 0) {
90 			while(val = strtok(NULL, " ,\t\n")) {
91 				if(strcmp(val, "real") == 0) {
92 					dtype_complex = 0;
93 				}
94 				if(strcmp(val, "complex") == 0) {
95 					dtype_complex = 1;
96 				}
97 			}
98 		} else if(strcmp(key, "No. Variables") == 0) {
99 			val = strtok(NULL, " \t\n");
100 			if(!val) {
101 				ss_msg(ERR, msgid, "%s:%d: syntax error, expected integer", name, lineno);
102 				g_free(line);
103 				return NULL;
104 			}
105 			nvars = atoi(val);
106 			got_nvars = 1;
107 		} else if(strcmp(key, "No. Points") == 0) {
108 			val = strtok(NULL, " \t\n");
109 			if(!val) {
110 				ss_msg(ERR, msgid, "%s:%d: syntax error, expected integer", name, lineno);
111 				g_free(line);
112 				return NULL;
113 			}
114 			npoints = atoi(val);
115 		} else if(strcmp(key, "Variables") == 0) {
116 			if(!got_nvars) {
117 				ss_msg(ERR, msgid, "%s:%d: \"Variables:\" before \"No. Variables:\"", name, lineno, i);
118 				goto err;
119 
120 			}
121 			sf = ss_new(fp, name, nvars-1, 0);
122 			sf->ncols = 1;
123 			sf->ntables = 1;
124 			/* first variable may be described on the same line
125 			 * as "Variables:" keyword
126 			 */
127 			vnum = strtok(NULL, " \t\n");
128 
129 			for(i = 0; i < nvars; i++) {
130 				if(i || !vnum) {
131 					if(fread_line(fp, &line, &linesize) == EOF) {
132 						ss_msg(ERR, msgid, "%s:%d: Unexpected EOF in \"Variables:\" at var %d", name, lineno, i);
133 						goto err;
134 					}
135 					lineno++;
136 					vnum = strtok(line, " \t\n");
137 				}
138 				vname = strtok(NULL, " \t\n");
139 				vtypestr = strtok(NULL, " \t\n");
140 				if(!vnum || !vname || !vtypestr) {
141 					ss_msg(ERR, msgid, "%s:%d: expected number name type", name, lineno);
142 					goto err;
143 				}
144 				if(i == 0) { /* assume Ind.Var. first */
145 					sf->ivar->name = g_strdup(vname);
146 					sf->ivar->type = sf_str2type_s3raw(vtypestr);
147 					sf->ivar->col = 0;
148 					/* ivar can't really be two-column,
149 					   this is a flag that says to
150 					   discard 2nd point */
151 					if(dtype_complex)
152 						sf->ivar->ncols = 2;
153 					else
154 						sf->ivar->ncols = 1;
155 
156 				} else {
157 					sf->dvar[i-1].name = g_strdup(vname);
158 					sf->dvar[i-1].type = sf_str2type_s3raw(vtypestr);
159 					sf->dvar[i-1].col = sf->ncols;
160 					if(dtype_complex)
161 						sf->dvar[i-1].ncols = 2;
162 					else
163 						sf->dvar[i-1].ncols = 1;
164 
165 					sf->ncols += sf->dvar[i-1].ncols;
166 				}
167 			}
168 		} else if(strcmp(key, "Values") == 0) {
169 			got_values = 1;
170 			break;
171 		} else if(strcmp(key, "Binary") == 0) {
172 			binary = 1;
173 			got_values = 1;
174 			break;
175 		}
176 		if(got_values)
177 			break;
178 	}
179 	if(!sf) {
180 		ss_msg(ERR, msgid, "%s:%d: no \"Variables:\" section in header", name, lineno);
181 		goto err;
182 	}
183 	if(!got_values) {
184 		ss_msg(ERR, msgid, "%s:%d: EOF without \"Values:\" in header", name, lineno);
185 		goto err;
186 	}
187 
188 	if(binary) {
189 		sf->readrow = sf_readrow_s3bin;
190 	} else {
191 		sf->readrow = sf_readrow_s3raw;
192 	}
193 	sf->read_rows = 0;
194 	sf->expected_vals = npoints * (sf->ncols + (dtype_complex ? 1 : 0));
195 	ss_msg(DBG, msgid, "expecting %d values\n", sf->expected_vals);
196 	sf->lineno = lineno;
197 	sf->linebuf = line;
198 	sf->lbufsize = linesize;
199 	ss_msg(DBG, msgid, "Done with header at offset 0x%lx\n", (long) ftello64(sf->fp));
200 
201 	return sf;
202 err:
203 	if(line)
204 		g_free(line);
205 	if(sf) {
206 		sf->fp = NULL;
207 		/* prevent ss_delete from cleaning up FILE*; ss_open callers
208 		   may rewind and try another format on failure. */
209 		ss_delete(sf);
210 	}
211 	return NULL;
212 }
213 
214 
215 /* return pointer to the next whitespace-seperated token in the file
216  * advances to the next lines of the file as needed.
217  * pointer points into the line buffer linebuf.
218  * token will not be nul-terminated; whole line remains available.
219  *
220  * upon return, sf->linep points to the char after the end of the token,
221  * which might be the trailing nul or might be whitespace.
222  */
sf_nexttoken(SpiceStream * sf)223 static char *sf_nexttoken(SpiceStream *sf)
224 {
225 	char *cp;
226 	char *tok = NULL;
227 
228 	if(sf->linep)
229 		cp = sf->linep;
230 	else {
231 		if(fread_line(sf->fp, &sf->linebuf, &sf->lbufsize) == EOF) {
232 			return 0;  /* normal EOF */
233 		}
234 		sf->lineno++;
235 		cp = sf->linebuf;
236 	}
237 
238 	// search for start of token
239 	while(!tok) {
240 		if(*cp == 0) {
241 			do {
242 				if(fread_line(sf->fp, &sf->linebuf, &sf->lbufsize) == EOF) {
243 					return 0;  /* normal EOF */
244 				}
245 				sf->lineno++;
246 				cp = sf->linebuf;
247 			} while (*cp == 0); // skip multiple blank lines
248 		}
249 		if(!isspace(*cp))
250 			tok = cp;
251 		else
252 			cp++;
253 	}
254 	// tok now points to start of the token; search for the end
255 	while(*cp && !isspace(*cp))
256 			cp++;
257 	sf->linep = cp;
258 	return tok;
259 }
260 
261 
262 /*
263  * Read row of values from an ascii spice3 raw file
264  */
265 static int
sf_readrow_s3raw(SpiceStream * sf,double * ivar,double * dvars)266 sf_readrow_s3raw(SpiceStream *sf, double *ivar, double *dvars)
267 {
268 	int i;
269 	int frownum;
270 	char *tok;
271 	double v;
272 
273 	if((sf->flags & SSF_PUSHBACK) == 0) {
274 		tok = sf_nexttoken(sf);
275 		if(!tok) {
276 			return 0;
277 //			ss_msg(ERR, msgid, "%s:%d: expected row number",
278 //			       sf->filename, sf->lineno);
279 //			return -1;
280 		}
281 		if(!isdigit(*tok)) {
282 			ss_msg(WARN, msgid, "%s:%d: expected row number, got \"%s\". Note: only one dataset per file is supported, extra garbage ignored",
283 			       sf->filename, sf->lineno, tok);
284 			return 0;
285 		}
286 		frownum = atoi(tok);
287 		/* todo: check for expected and maximum row number */
288 
289 		tok = sf_nexttoken(sf);
290 		if(!tok) {
291 			ss_msg(WARN, msgid, "%s:%d: expected ivar value",
292 			       sf->filename, sf->lineno);
293 			return -1;
294 		}
295 		v = atof(tok);
296 		if(v < sf->ivval) {
297 			/* independent-variable value decreased, this must
298 			 * be the start of another sweep.  hold the value and
299 			 * return flag to caller.
300 			 */
301 			sf->ivval = v;
302 			sf->flags |= SSF_PUSHBACK;
303 			return -2;
304 		} else {
305 			sf->ivval = v;
306 			*ivar = v;
307 		}
308 	} else {
309 		/* iv value for start of new sweep was read last time */
310 		sf->flags &= ~SSF_PUSHBACK;
311 		*ivar = sf->ivval;
312 	}
313 
314 	for(i = 0; i < sf->ndv; i++) {
315 		SpiceVar *dv;
316 		dv = &sf->dvar[i];
317 
318 		tok = sf_nexttoken(sf);
319 		if(!tok) {
320 			ss_msg(ERR, msgid, "%s:%d: expected value",
321 			       sf->filename, sf->lineno);
322 			return -1;
323 		}
324 		dvars[dv->col-1] = atof(tok);
325 
326 		if(dv->ncols > 1) {
327 			tok = strchr(tok, ',');
328 			if(!tok || !*(tok+1)) {
329 				ss_msg(ERR, msgid, "%s:%d: expected second value",
330 				       sf->filename, sf->lineno);
331 				return -1;
332 			}
333 			tok++;
334 			dvars[dv->col] = atof(tok);
335 		}
336 	}
337 	sf->read_rows++;
338 	return 1;
339 }
340 
341 /*
342  * Read a single value from binary spice3 rawfile, and do
343  * the related error-checking.
344  */
345 
346 static int
sf_getval_s3bin(SpiceStream * sf,double * dval)347 sf_getval_s3bin(SpiceStream *sf, double *dval)
348 {
349 	off64_t pos;
350 	double val;
351 
352 	if(sf->read_vals >= sf->expected_vals) {
353 		pos = ftello64(sf->fp);
354 		ss_msg(DBG, "sf_getval_s3bin", "past last expected value offset 0x%lx", (long) pos);
355 		return 0;
356 	}
357 	if(fread(&val, sizeof(double), 1, sf->fp) != 1) {
358 		pos = ftello64(sf->fp);
359 		ss_msg(ERR, "sf_getval_s3bin", "unexepected EOF in data at offset 0x%lx", (long) pos);
360 		return -1;
361 	}
362 	sf->read_vals++;
363 
364 	*dval = val;
365 	return 1;
366 }
367 
368 
369 /*
370  * Read row of values from a binay spice3 raw file
371  */
372 static int
sf_readrow_s3bin(SpiceStream * sf,double * ivar,double * dvars)373 sf_readrow_s3bin(SpiceStream *sf, double *ivar, double *dvars)
374 {
375 	int i, rc;
376 	double v;
377 	double dummy;
378 
379 	if((sf->flags & SSF_PUSHBACK) == 0) {
380 		rc = sf_getval_s3bin(sf, &v);
381 		if(rc == 0)		/* file EOF */
382 			return 0;
383 		if(rc < 0)
384 			return -1;
385 		if(sf->ivar->ncols == 2) {
386 			rc = sf_getval_s3bin(sf, &dummy);
387 			if(rc == 0)		/* file EOF */
388 				return 0;
389 			if(rc < 0)
390 				return -1;
391 		}
392 		if(v < sf->ivval) {
393 			/* independent-variable value decreased, this must
394 			 * be the start of another sweep.  hold the value and
395 			 * return flag to caller.
396 			 */
397 			sf->ivval = v;
398 			sf->flags |= SSF_PUSHBACK;
399 			return -2;
400 		} else {
401 			sf->ivval = v;
402 			*ivar = v;
403 		}
404 	} else {
405 		/* iv value for start of new sweep was read last time */
406 		sf->flags &= ~SSF_PUSHBACK;
407 		*ivar = sf->ivval;
408 	}
409 
410 	for(i = 0; i < sf->ncols-1; i++) {
411 		if(sf_getval_s3bin(sf, &dvars[i]) != 1) {
412 			ss_msg(WARN, "sf_readrow_s3bin", "%s: EOF or error reading data field %d in row %d; file is incomplete.", sf->filename, i, sf->read_rows);
413 			return 0;
414 		}
415 	}
416 
417 	sf->read_rows++;
418 	return 1;
419 }
420