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