1 /*
2  * SpiceStream - simple, incremental reader for analog data files,
3  * such as those produced by spice-type simulators.
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 General Public License
18  * 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 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <float.h>
29 #include <stdarg.h>
30 #include <errno.h>
31 #include <config.h>
32 #include <glib.h>
33 
34 #include "spicestream.h"
35 
36 extern SpiceStream *sf_rdhdr_hspice(char *name, FILE *fp);
37 extern SpiceStream *sf_rdhdr_hsascii(char *name, FILE *fp);
38 extern SpiceStream *sf_rdhdr_hsbin(char *name, FILE *fp);
39 extern SpiceStream *sf_rdhdr_cazm(char *name, FILE *fp);
40 extern SpiceStream *sf_rdhdr_s3raw(char *name, FILE *fp);
41 extern SpiceStream *sf_rdhdr_s2raw(char *name, FILE *fp);
42 extern SpiceStream *sf_rdhdr_ascii(char *name, FILE *fp);
43 extern SpiceStream *sf_rdhdr_nsout(char *name, FILE *fp);
44 static int ss_readrow_none(SpiceStream *, double *ivar, double *dvars);
45 
46 SSMsgLevel spicestream_msg_level = WARN;
47 
48 typedef SpiceStream* (*PFD)(char *name, FILE *fp);
49 
50 typedef struct {
51 	char *name;
52 	PFD rdfunc;
53 } DFormat;
54 
55 static DFormat format_tab[] = {
56 	{"hspice", sf_rdhdr_hspice },
57 	{"hsascii", sf_rdhdr_hsascii },
58 	{"hsbinary", sf_rdhdr_hsbin },
59 	{"cazm", sf_rdhdr_cazm },
60 	{"spice3raw", sf_rdhdr_s3raw },
61 	{"spice2raw", sf_rdhdr_s2raw },
62 	{"ascii", sf_rdhdr_ascii },
63 	{"nsout", sf_rdhdr_nsout },
64 };
65 static const int NFormats = sizeof(format_tab)/sizeof(DFormat);
66 
67 /*
68  * Open spice waveform file for reading.
69  * Reads in header with signal names (and sometimes signal types).
70  * TODO: simple strategies for trying to deduce file type from
71  * name or contents.
72  */
73 
74 SpiceStream *
ss_open_internal(FILE * fp,char * filename,char * format)75 ss_open_internal(FILE *fp, char *filename, char *format)
76 {
77 	SpiceStream *ss;
78 	int i;
79 
80 	for(i = 0; i < NFormats; i++) {
81 		if(0==strcmp(format, format_tab[i].name)) {
82 			ss = (format_tab[i].rdfunc)(filename, fp);
83 			if(ss) {
84 				ss->filetype = i;
85 				return ss;
86 			} else {
87 				ss_msg(DBG, "ss_open", "failed to open %s using format %s", filename, format_tab[i].name);
88 				return NULL;
89 			}
90 		}
91 	}
92 	ss_msg(ERR, "ss_open", "Format \"%s\" unknown", format);
93 	return NULL;
94 }
95 
96 SpiceStream *
ss_open(char * filename,char * format)97 ss_open(char *filename, char *format)
98 {
99 	FILE *fp;
100 
101 	fp = fopen64(filename, "r");
102 	if(fp == NULL) {
103 		fprintf(stderr, "fopen(\"%s\"): %s\n", filename, strerror(errno));
104 		return NULL;
105 	}
106 
107 	return ss_open_internal(fp, filename, format);
108 }
109 
110 SpiceStream *
ss_open_fp(FILE * fp,char * format)111 ss_open_fp(FILE *fp, char *format)
112 {
113 	return ss_open_internal(fp, "<spicestream>", format);
114 }
115 
116 /*
117  * Allocate SpiceStream structure and fill in some portions.
118  * To be called only from format-specific header-reading functions,
119  * usually after they read and verify the header.
120  * Caller must still set types and names of ivar and dvars,
121  * and must set readrow and linebuf items.
122  */
123 SpiceStream *
ss_new(FILE * fp,char * filename,int ndv,int nspar)124 ss_new(FILE *fp, char *filename, int ndv, int nspar)
125 {
126 	SpiceStream *ss;
127 
128 	ss = g_new0(SpiceStream, 1);
129 	ss->filename = g_strdup(filename);
130 	ss->fp = fp;
131 	ss->ivar = g_new0(SpiceVar, 1);
132 	ss->ndv = ndv;
133 	if(ndv)
134 		ss->dvar = g_new0(SpiceVar, ndv);
135 	ss->nsweepparam = nspar;
136 	if(nspar)
137 		ss->spar = g_new0(SpiceVar, nspar);
138 
139 	return ss;
140 }
141 
142 /*
143  * Close the file assocated with a SpiceStream.
144  * No more data can be read, but the header information can still
145  * be accessed.
146  */
ss_close(SpiceStream * ss)147 void ss_close(SpiceStream *ss)
148 {
149 	fclose(ss->fp);
150 	ss->fp = NULL;
151 	ss->readrow = ss_readrow_none;
152 }
153 
154 /*
155  * Free all resources associated with a SpiceStream.
156  */
ss_delete(SpiceStream * ss)157 void ss_delete(SpiceStream *ss)
158 {
159 	if(ss->fp)
160 		fclose(ss->fp);
161 	if(ss->filename)
162 		g_free(ss->filename);
163 	if(ss->ivar)
164 		g_free(ss->ivar);
165 	if(ss->dvar)
166 		g_free(ss->dvar);
167 	if(ss->linebuf)
168 		g_free(ss->linebuf);
169 	g_free(ss);
170 }
171 
172 /*
173  * row-reading function that always returns EOF.
174  */
175 static int
ss_readrow_none(SpiceStream * ss,double * ivar,double * dvars)176 ss_readrow_none(SpiceStream *ss, double *ivar, double *dvars)
177 {
178 	return 0;
179 }
180 
181 
182 static char *vartype_names[] = {
183 	"Unknown", "Time", "Voltage", "Current", "Frequency"
184 };
185 const int nvartype_names = sizeof(vartype_names)/sizeof(char *);
186 
187 /*
188  * return a string corresponding to a SpiceStream VarType.
189  * the pointer returned is in static or readonly storage,
190  * and is overwritten with each call.
191  */
vartype_name_str(VarType type)192 char *vartype_name_str(VarType type)
193 {
194 	static char buf[32];
195 	if(type < nvartype_names)
196 		return vartype_names[type];
197 	else {
198 		sprintf(buf, "type-%d", type);
199 		return buf;
200 	}
201 }
202 
203 /*
204  * return pointer to string with printable name for a variable
205  * or one of the columns of a variable.
206  * buf is a pointer to a buffer to use.  If NULL, one will be allocated.
207  * n is the maximum number of characters to put in the buffer.
208  */
ss_var_name(SpiceVar * sv,int col,char * buf,int n)209 char *ss_var_name(SpiceVar *sv, int col, char *buf, int n)
210 {
211 	int idx;
212 
213 	if(buf == NULL) {
214 		int l;
215 		l = strlen(sv->name + 3);
216 		buf = g_new(char, l);
217 		n = l;
218 	}
219 	strncpy(buf, sv->name, n-1);
220 	n -= strlen(buf)+1;
221 	if(sv->ncols == 1 || col < 0)
222 		return buf;
223 	if(n>1) {
224 		idx = strlen(buf);
225 		buf[idx++] = '.';
226 		buf[idx++] = '0'+col;
227 		buf[idx] = 0;
228 	}
229 
230 	return(buf);
231 }
232 
233 /*
234  * given a filetype number, return a pointer to a string containing the
235  * name of the Spicestream file format.
236  * Valid file type numbers start at 0.
237  */
ss_filetype_name(int n)238 char *ss_filetype_name(int n)
239 {
240 	if(n >= 0 && n < NFormats)
241 		return format_tab[n].name;
242 	else
243 		return NULL;
244 }
245 
246 /*
247  * utility function to read whole line into buffer, expanding buffer if needed.
248  * line buffer must be allocated with g_malloc/g_new, or NULL in which case
249  * we allocate an initial, buffer.
250  * returns 0 or EOF.
251  */
252 int
fread_line(FILE * fp,char ** bufp,int * bufsize)253 fread_line(FILE *fp, char **bufp, int *bufsize)
254 {
255 	int c;
256 	int n = 0;
257 	if(*bufp == NULL) {
258 		if(*bufsize == 0)
259 			*bufsize = 1024;
260 		*bufp = g_new(char, *bufsize);
261 	}
262 	while(((c = getc(fp)) != EOF) && c != '\n') {
263 		(*bufp)[n++] = c;
264 		if(n >= *bufsize) {
265 			*bufsize *= 2;
266 			*bufp = g_realloc(*bufp, *bufsize);
267 		}
268 	}
269 	(*bufp)[n] = 0;
270 	if(c == EOF)
271 		return EOF;
272 	else
273 		return 0;
274 }
275 
276 FILE *ss_error_file;
277 SSMsgHook ss_error_hook;
278 
279 /*
280  * ss_msg: emit an error message from anything in the spicestream subsystem,
281  * or anything else that wants to use our routines.
282  *
283  * If ss_error_hook is non-NULL, it is a pointer to a function that
284  * will be called with the error string.
285  * if ss_error_file is non-NULL, it is a FILE* to write the message to.
286  * If neither of these are non-null, the message is written to stderr.
287  *
288  * args:
289  *   type is one of:
290  *	 DBG - Debug, ERR - ERROR, INFO - infomration, WARN - warning
291  *       id is the name of the function, or other identifier
292  * 	 remaining arguments are printf-like.
293  */
294 void
ss_msg(SSMsgLevel type,const char * id,const char * msg,...)295 ss_msg(SSMsgLevel type, const char *id, const char *msg, ...)
296 {
297 	char *typestr;
298 	va_list args;
299 	int blen = 1024;
300 	char buf[1024];
301 
302 	if(type < spicestream_msg_level)
303 		return;
304 
305 	switch (type) {
306 	case DBG:
307 		typestr = "<<DEBUG>>";
308 		break;
309 	case ERR:
310 		typestr = "<<ERROR>>";
311 		break;
312 	case WARN:
313 		typestr = "<<WARNING>>";
314 		break;
315 	case INFO:
316 	default:
317 		typestr = "";
318 		break;
319 	}
320 
321 	va_start(args, msg);
322 
323 #ifdef HAVE_SNPRINTF
324 	blen = snprintf(buf, 1024, "[%s]: %s ", id, typestr);
325 	if(blen>0)
326 		blen += vsnprintf(&buf[blen-1], 1024-blen, msg, args);
327 	if(blen>0)
328 		blen += snprintf(&buf[blen-1], 1024-blen, "\n");
329 #else
330 	sprintf(buf, "[%s]: %s ", id, typestr);
331 	blen = strlen(buf);
332 	vsprintf(&buf[blen], msg, args);
333 	strcat(buf, "\n");
334 #endif
335 
336 	if(ss_error_hook)
337 		(ss_error_hook)(buf);
338 	if(ss_error_file)
339 		fputs(buf, ss_error_file);
340 	if(ss_error_hook == NULL && ss_error_file == NULL)
341 		fputs(buf, stderr);
342 
343 	va_end(args);
344 }
345