Annotation of OpenXM_contrib/gnuplot/datafile.c, Revision 1.1.1.2
1.1 maekawa 1: #ifndef lint
1.1.1.2 ! maekawa 2: static char *RCSid = "$Id: datafile.c,v 1.11.2.1 1999/08/19 14:41:09 lhecking Exp $";
1.1 maekawa 3: #endif
4:
5: /* GNUPLOT - datafile.c */
6:
7: /*[
8: * Copyright 1986 - 1993, 1998 Thomas Williams, Colin Kelley
9: *
10: * Permission to use, copy, and distribute this software and its
11: * documentation for any purpose with or without fee is hereby granted,
12: * provided that the above copyright notice appear in all copies and
13: * that both that copyright notice and this permission notice appear
14: * in supporting documentation.
15: *
16: * Permission to modify the software is granted, but not the right to
17: * distribute the complete modified source code. Modifications are to
18: * be distributed as patches to the released version. Permission to
19: * distribute binaries produced by compiling modified sources is granted,
20: * provided you
21: * 1. distribute the corresponding source modifications from the
22: * released version in the form of a patch file along with the binaries,
23: * 2. add special version identification to distinguish your version
24: * in addition to the base release version number,
25: * 3. provide your name and address as the primary contact for the
26: * support of your modified version, and
27: * 4. retain our contact information in regard to use of the base
28: * software.
29: * Permission to distribute the released version of the source code along
30: * with corresponding source modifications in the form of a patch file is
31: * granted with same provisions 2 through 4 for binary distributions.
32: *
33: * This software is provided "as is" without express or implied warranty
34: * to the extent permitted by applicable law.
35: ]*/
36:
37: /* AUTHOR : David Denholm */
38:
39: /*
40: * this file provides the functions to handle data-file reading..
41: * takes care of all the pipe / stdin / index / using worries
42: */
43:
44: /*{{{ notes */
45: /* couldn't decide how to implement 'thru' only for 2d and 'index'
46: * for only 3d, so I did them for both - I can see a use for
47: * index in 2d, especially for fit.
48: *
49: * I keep thru for backwards compatibility, and extend it to allow
50: * more natural plot 'data' thru f(y) - I (personally) prefer
51: * my syntax, but then I'm biased...
52: *
53: * - because I needed it, I have added a range of indexes...
54: * (s)plot 'data' [index i[:j]]
55: *
56: * also every a:b:c:d:e:f - plot every a'th point from c to e,
57: * in every b lines from d to f
58: * ie for (line=d; line<=f; line+=b)
59: * for (point=c; point >=e; point+=a)
60: *
61: *
62: * I dont like mixing this with the time series hack... I am
63: * very into modular code, so I would prefer to not have to
64: * have _anything_ to do with time series... for example,
65: * we just look at columns in file, and that is independent
66: * of 2d/3d. I really dont want to have to pass a flag to
67: * this is plot or splot.
68: *
69: * use a global array df_timecol[] - cleared by df_open, then
70: * columns needing time set by client.
71: *
72: * Now that df_2dbinary() and df_3dbinary() are here, I am seriously
73: * tempted to move get_data() and get_3ddata() in here too
74: *
75: * public variables declared in this file.
76: * int df_no_use_specs - number of columns specified with 'using'
77: * int df_line_number - for error reporting
78: * int df_datum - increases with each data point
79: * TBOOLEAN df_binary - it's a binary file
80: * [ might change this to return value from df_open() ]
81: * int df_eof - end of file
82: * int df_timecol[] - client controls which cols read as time
83: *
84: * functions
85: * int df_open(int max_using)
86: * parses thru / index / using on command line
87: * max_using is max no of 'using' columns allowed
88: * returns number of 'using' cols specified, or -1 on error (?)
89: *
90: * int df_readline(double vector[], int max)
91: * reads a line, does all the 'index' and 'using' manipulation
92: * deposits values into vector[]
93: * returns
94: * number of columns parsed [0=not blank line, but no valid data],
95: * DF_EOF for EOF
96: * DF_UNDEFINED - undefined result during eval of extended using spec
97: * DF_FIRST_BLANK for first consecutive blank line
98: * DF_SECOND_BLANK for second consecutive blank line
99: * will return FIRST before SECOND
100: *
101: * if a using spec was given, lines not fulfilling spec are ignored.
102: * we will always return exactly the number of items specified
103: *
104: * if no spec given, we return number of consecutive columns we parsed.
105: *
106: * if we are processing indexes, seperated by 'n' blank lines,
107: * we will return n-1 blank lines before noticing the index change
108: *
109: * void df_close()
110: * closes a currently open file.
111: *
112: * void f_dollars(x)
113: * void f_column() actions for expressions using $i, column(j), etc
114: * void f_valid()
115: *
116: *
117: * line parsing slightly differently from previous versions of gnuplot...
118: * given a line containing fewer columns than asked for, gnuplot used to make
119: * up values... I say that if I have explicitly said 'using 1:2:3', then if
120: * column 3 doesn't exist, I dont want this point...
121: *
122: * a column number of 0 means generate a value... as before, this value
123: * is useful in 2d as an x value, and is reset at blank lines.
124: * a column number of -1 means the (data) line number (not the file line
125: * number). splot 'file' using 1 is equivalent to
126: * splot 'file' using 0:-1:1
127: * column number -2 is the index. It was put in to kludge multi-branch
128: * fitting.
129: *
130: * 20/5/95 : accept 1.23d4 in place of e (but not in scanf string)
131: * : autoextend data line buffer and MAX_COLS
132: *
133: * 11/8/96 : add 'columns' -1 for suggested y value, and -2 for
134: * current index.
135: * using 1:-1:-2 and column(-1) are supported.
136: * $-1 and $-2 are not yet supported, because of the
137: * way the parser works
138: *
139: */
140: /*}}} */
141:
142: #include "plot.h"
143: #include "fnproto.h" /* check prototypes against our defns */
144: #include "binary.h"
145: #include "setshow.h"
146:
147: /* if you change this, change the scanf in readline */
148: #define NCOL 7 /* max using specs */
149:
150: /*{{{ static fns */
151: #if 0 /* not used */
152: static int get_time_cols __PROTO((char *fmt));
153: static void mod_def_usespec __PROTO((int specno, int jump));
154: #endif
155: static int check_missing __PROTO((char *s));
156: static char *df_gets __PROTO((void));
157: static int df_tokenise __PROTO((char *s));
158: static float **df_read_matrix __PROTO((int *rows, int *columns));
159: /*}}} */
160:
161: /*{{{ variables */
162: struct use_spec_s {
163: int column;
164: struct at_type *at;
165: };
166:
167: /* public variables client might access */
168:
169: int df_no_use_specs; /* how many using columns were specified */
170: int df_line_number;
171: int df_datum; /* suggested x value if none given */
172: TBOOLEAN df_matrix = FALSE; /* is this a matrix splot */
173: int df_eof = 0;
174: int df_timecol[NCOL];
175: TBOOLEAN df_binary = FALSE; /* this is a binary file */
176:
177: /* private variables */
178:
179: /* in order to allow arbitrary data line length, we need to use the heap
180: * might consider free-ing it in df_close, especially for small systems
181: */
182: static char *line = NULL;
183: static int max_line_len = 0;
184:
185: static FILE *data_fp = NULL;
186: static TBOOLEAN pipe_open = FALSE;
187: static TBOOLEAN mixed_data_fp = FALSE;
188:
189: #ifndef MAXINT /* should there be one already defined ? */
190: # ifdef INT_MAX /* in limits.h ? */
191: # define MAXINT INT_MAX
192: # else
193: # define MAXINT ((~0)>>1)
194: # endif
195: #endif
196:
197: /* stuff for implementing index */
198: static int blank_count = 0; /* how many blank lines recently */
199: static int df_lower_index = 0; /* first mesh required */
200: static int df_upper_index = MAXINT;
201: static int df_index_step = 1; /* 'every' for indices */
202: static int df_current_index; /* current mesh */
203:
204: /* stuff for every point:line */
205: static int everypoint = 1;
206: static int firstpoint = 0;
207: static int lastpoint = MAXINT;
208: static int everyline = 1;
209: static int firstline = 0;
210: static int lastline = MAXINT;
211: static int point_count = -1; /* point counter - preincrement and test 0 */
212: static int line_count = 0; /* line counter */
213:
214: /* parsing stuff */
215: static struct use_spec_s use_spec[NCOL];
216: static char df_format[MAX_LINE_LEN + 1];
217:
218: /* rather than three arrays which all grow dynamically, make one
219: * dynamic array of this structure
220: */
221:
222: typedef struct df_column_struct {
223: double datum;
224: enum {
225: DF_MISSING, DF_BAD, DF_GOOD
226: } good;
227: char *position;
228: } df_column_struct;
229:
230: static df_column_struct *df_column = NULL; /* we'll allocate space as needed */
231: static int df_max_cols = 0; /* space allocated */
232: static int df_no_cols; /* cols read */
233: static int fast_columns; /* corey@cac optimization */
234:
235: /* external variables we need */
236:
237: extern int c_token, num_tokens;
238: extern char timefmt[]; /* I would rather not need this, but ... */
239: /* columns needing timefmt are passed in df_timecol[] after df_open */
240:
241: /* jev -- for passing data thru user-defined function */
242: extern struct udft_entry ydata_func;
243: extern struct udft_entry *dummy_func;
244: extern char dummy_var[MAX_NUM_VAR][MAX_ID_LEN + 1];
245: extern char c_dummy_var[MAX_NUM_VAR][MAX_ID_LEN + 1];
246:
247: extern double min_array[], max_array[];
248: /*}}} */
249:
250:
251: /*{{{ static char *df_gets() */
252: static char *df_gets()
253: {
254: int len = 0;
255:
256: if (!fgets(line, max_line_len, data_fp))
257: return NULL;
258:
259: if (mixed_data_fp)
260: ++inline_num;
261:
262: for (;;) {
263: len += strlen(line + len);
264:
265: if (len > 0 && line[len - 1] == '\n') {
266: /* we have read an entire text-file line.
267: * Strip the trailing linefeed and return
268: */
269: line[len - 1] = 0;
270: return line;
271: }
272: /* buffer we provided may not be full - dont grab extra
273: * memory un-necessarily. This may trap a problem with last
274: * line in file not being properly terminated - each time
275: * through a replot loop, it was doubling buffer size
276: */
277:
278: if ((max_line_len - len) < 32)
279: line = gp_realloc(line, max_line_len *= 2, "datafile line buffer");
280:
281: if (!fgets(line + len, max_line_len - len, data_fp))
282: return line; /* unexpected end of file, but we have something to do */
283: }
284:
285: /* NOTREACHED */
286: return NULL;
287: }
288: /*}}} */
289:
290: /*{{{ static int df_tokenise(s) */
291: static int df_tokenise(s)
292: char *s;
293: {
294: /* implement our own sscanf that takes 'missing' into account,
295: * and can understand fortran quad format
296: */
297:
298: df_no_cols = 0;
299:
300: while (*s) {
301:
302: /* check store - double max cols or add 20, whichever is greater */
303: if (df_max_cols <= df_no_cols)
304: df_column = (df_column_struct *) gp_realloc(df_column, (df_max_cols += (df_max_cols < 20 ? 20 : df_max_cols)) * sizeof(df_column_struct), "datafile column");
305:
306: /* have always skipped spaces at this point */
307: df_column[df_no_cols].position = s;
308:
309: if (check_missing(s))
310: df_column[df_no_cols].good = DF_MISSING;
311: else {
312: #ifdef OSK
313: /* apparently %n does not work. This implementation
314: * is just as good as the non-OSK one, but close
315: * to a release (at last) we make it os-9 specific
316: */
317: int count;
318: char *p = strpbrk(s, "dqDQ");
319: if (p != NULL)
320: *p = 'e';
321:
322: count = sscanf(s, "%lf", &df_column[df_no_cols].datum);
323: #else
324: /* cannot trust strtod - eg strtod("-",&p) */
325: int used;
326: int count;
327: int dfncp1 = df_no_cols + 1;
328:
329: /*
330: * optimizations by Corey Satten, corey@cac.washington.edu
331: */
332: if (fast_columns == 0 ||
333: df_no_use_specs > 0 && (use_spec[0].column == dfncp1 ||
334: df_no_use_specs > 1 && (use_spec[1].column == dfncp1 ||
335: df_no_use_specs > 2 && (use_spec[2].column == dfncp1 ||
336: df_no_use_specs > 3 && (use_spec[3].column == dfncp1 ||
337: df_no_use_specs > 4 && (use_spec[4].column == dfncp1 ||
338: df_no_use_specs > 5))))) ||
339: df_no_use_specs == 0) {
340:
341: #ifndef NO_FORTRAN_NUMS
342: count = sscanf(s, "%lf%n", &df_column[df_no_cols].datum, &used);
343: #else
344: while (isspace(*s))
345: ++s;
346: count = *s ? 1 : 0;
347: df_column[df_no_cols].datum = atof(s);
348: #endif /* NO_FORTRAN_NUMS */
349: } else {
350: /* skip any space at start of column */
1.1.1.2 ! maekawa 351: /* HBB tells me that the cast must be to
! 352: * unsigned char instead of int. */
! 353: while (isspace((unsigned char) *s))
1.1 maekawa 354: ++s;
355: count = *s ? 1 : 0;
356: /* skip chars to end of column */
1.1.1.2 ! maekawa 357: used = 0;
! 358: while (!isspace((unsigned char) *s) && (*s != NUL))
! 359: ++s;
1.1 maekawa 360: }
361:
362: /* it might be a fortran double or quad precision.
363: * 'used' is only safe if count is 1
364: */
365:
366: #ifndef NO_FORTRAN_NUMS
367: if (count == 1 &&
368: (s[used] == 'd' || s[used] == 'D' ||
369: s[used] == 'q' || s[used] == 'Q')) {
370: /* might be fortran double */
371: s[used] = 'e';
372: /* and try again */
373: count = sscanf(s, "%lf", &df_column[df_no_cols].datum);
374: }
375: #endif /* NO_FORTRAN_NUMS */
376: #endif /* OSK */
377: df_column[df_no_cols].good = count == 1 ? DF_GOOD : DF_BAD;
378: }
379:
380: ++df_no_cols;
381: /*{{{ skip chars to end of column */
382: while ((!isspace((int)*s)) && (*s != '\0'))
383: ++s;
384: /*}}} */
385: /*{{{ skip spaces to start of next column */
386: while (isspace((int)*s))
387: ++s;
388: /*}}} */
389: }
390:
391: return df_no_cols;
392: }
393: /*}}} */
394:
395: /*{{{ static float **df_read_matrix() */
396: /* reads a matrix from a text file
397: * stores in same storage format as fread_matrix
398: */
399:
400: static float **df_read_matrix(rows, cols)
401: int *rows, *cols;
402: {
403: int max_rows = 0;
404: int c;
405: float **rmatrix = NULL;
406:
407: char *s;
408:
409: *rows = 0;
410: *cols = 0;
411:
412: for (;;) {
413: if (!(s = df_gets())) {
414: df_eof = 1;
415: return rmatrix; /* NULL if we have not read anything yet */
416: }
417: while (isspace((int)*s))
418: ++s;
419:
420: if (!*s || is_comment(*s)) {
421: if (rmatrix)
422: return rmatrix;
423: else
424: continue;
425: }
426: if (mixed_data_fp && is_EOF(*s)) {
427: df_eof = 1;
428: return rmatrix;
429: }
430: c = df_tokenise(s);
431:
432: if (!c)
433: return rmatrix;
434:
435: if (*cols && c != *cols) {
436: /* its not regular */
437: int_error("Matrix does not represent a grid", NO_CARET);
438: }
439: *cols = c;
440:
441: if (*rows >= max_rows) {
442: rmatrix = gp_realloc(rmatrix, (max_rows += 10) * sizeof(float *), "df_matrix");
443: }
444: /* allocate a row and store data */
445: {
446: int i;
447: float *row = rmatrix[*rows] = (float *) gp_alloc(c * sizeof(float), "df_matrix row");
448:
449: for (i = 0; i < c; ++i) {
450: if (df_column[i].good != DF_GOOD)
451: int_error("Bad number in matrix", NO_CARET);
452:
453: row[i] = (float) df_column[i].datum;
454: }
455:
456: ++*rows;
457: }
458: }
459: }
460:
461: /*}}} */
462:
463:
464: /*{{{ int df_open(max_using) */
465: int df_open(max_using)
466: int max_using;
467:
468: /* open file, parsing using/thru/index stuff
469: * return number of using specs [well, we have to return something !]
470: */
471:
472: {
1.1.1.2 ! maekawa 473: /* now allocated dynamically */
! 474: static char *filename = NULL;
1.1 maekawa 475: int i;
476: int name_token;
477:
478: fast_columns = 1; /* corey@cac */
479:
480: /*{{{ close file if necessary */
481: if (data_fp)
482: df_close();
483: /*}}} */
484:
485: /*{{{ initialise static variables */
486: df_format[0] = NUL; /* no format string */
487:
488: df_no_use_specs = 0;
489:
490: for (i = 0; i < NCOL; ++i) {
491: use_spec[i].column = i + 1; /* default column */
492: use_spec[i].at = NULL; /* no expression */
493: }
494:
495: if (max_using > NCOL)
496: max_using = NCOL;
497:
498: df_datum = -1; /* it will be preincremented before use */
499: df_line_number = 0; /* ditto */
500:
501: df_lower_index = 0;
502: df_index_step = 1;
503: df_upper_index = MAXINT;
504:
505: df_current_index = 0;
506: blank_count = 2;
507: /* by initialising blank_count, leading blanks will be ignored */
508:
509: everypoint = everyline = 1; /* unless there is an every spec */
510: firstpoint = firstline = 0;
511: lastpoint = lastline = MAXINT;
512:
513: df_eof = 0;
514:
515: memset(df_timecol, 0, sizeof(df_timecol));
516:
517: df_binary = 1;
518: /*}}} */
519:
520: assert(max_using <= NCOL);
521:
522: /* empty name means re-use last one */
1.1.1.2 ! maekawa 523: if (isstring(c_token) && token_len(c_token) == 2) {
! 524: if (!filename || !*filename)
! 525: int_error("No previous filename",c_token);
! 526: } else {
! 527: filename = gp_realloc(filename, token_len(c_token), "datafile name");
! 528: quote_str(filename, c_token, token_len(c_token));
1.1 maekawa 529: }
530: name_token = c_token++;
531:
532: /* defer opening until we have parsed the modifiers... */
533:
534: /*{{{ look for binary / matrix */
535: df_binary = df_matrix = FALSE;
536:
537: if (almost_equals(c_token, "bin$ary")) {
538: ++c_token;
539: df_binary = TRUE;
540: df_matrix = TRUE;
541: } else if (almost_equals(c_token, "mat$rix")) {
542: ++c_token;
543: df_matrix = TRUE;
544: }
545: /*}}} */
546:
547: /*{{{ deal with index */
548: if (almost_equals(c_token, "i$ndex")) {
549: struct value a;
550:
551: if (df_binary)
552: int_error("Binary file format does not allow more than one surface per file", c_token);
553:
554: ++c_token;
555: df_lower_index = (int) real(const_express(&a));
556: if (equals(c_token, ":")) {
557: ++c_token;
558: df_upper_index = (int) magnitude(const_express(&a));
559: if (df_upper_index < df_lower_index)
560: int_error("Upper index should be bigger than lower index", c_token);
561:
562: if (equals(c_token, ":")) {
563: ++c_token;
564: df_index_step = (int) magnitude(const_express(&a));
565: if (df_index_step < 1)
566: int_error("Index step must be positive", c_token);
567: }
568: } else
569: df_upper_index = df_lower_index;
570: }
571: /*}}} */
572:
573: /*{{{ deal with every */
574: if (almost_equals(c_token, "ev$ery")) {
575: struct value a;
576:
577: fast_columns = 0; /* corey@cac */
578: /* allow empty fields - every a:b:c::e
579: * we have already established the defaults
580: */
581:
582: if (!equals(++c_token, ":")) {
583: everypoint = (int) real(const_express(&a));
584: if (everypoint < 1)
585: int_error("Expected positive integer", c_token);
586: }
587: /* if it fails on first test, no more tests will succeed. If it
588: * fails on second test, next test will succeed with correct c_token
589: */
590: if (equals(c_token, ":") && !equals(++c_token, ":")) {
591: everyline = (int) real(const_express(&a));
592: if (everyline < 1)
593: int_error("Expected positive integer", c_token);
594: }
595: if (equals(c_token, ":") && !equals(++c_token, ":")) {
596: firstpoint = (int) real(const_express(&a));
597: if (firstpoint < 0)
598: int_error("Expected non-negative integer", c_token);
599: }
600: if (equals(c_token, ":") && !equals(++c_token, ":")) {
601: firstline = (int) real(const_express(&a));
602: if (firstline < 0)
603: int_error("Expected non-negative integer", c_token);
604: }
605: if (equals(c_token, ":") && !equals(++c_token, ":")) {
606: lastpoint = (int) real(const_express(&a));
607: if (lastpoint < firstpoint)
608: int_error("Last point must not be before first point", c_token);
609: }
610: if (equals(c_token, ":")) {
611: ++c_token;
612: lastline = (int) real(const_express(&a));
613: if (lastline < firstline)
614: int_error("Last line must not be before first line", c_token);
615: }
616: }
617: /*}}} */
618:
619: /*{{{ deal with thru */
620: /* jev -- support for passing data from file thru user function */
621:
622: if (almost_equals(c_token, "thru$")) {
623: c_token++;
624: if (ydata_func.at)
625: free(ydata_func.at);
626: strcpy(c_dummy_var[0], dummy_var[0]);
627: /* allow y also as a dummy variable.
628: * during plot, c_dummy_var[0] and [1] are 'sacred'
629: * ie may be set by splot [u=1:2] [v=1:2], and these
630: * names are stored only in c_dummy_var[]
631: * so choose dummy var 2 - can anything vital be here ?
632: */
633: dummy_func = &ydata_func;
634: strcpy(c_dummy_var[2], "y");
635: ydata_func.at = perm_at();
636: dummy_func = NULL;
637: } else {
638: if (ydata_func.at)
639: free(ydata_func.at);
640: ydata_func.at = NULL;
641: }
642: /*}}} */
643:
644: /*{{{ deal with using */
645: if (almost_equals(c_token, "u$sing")) {
646: if (!END_OF_COMMAND && !isstring(++c_token)) {
647: struct value a;
648:
649: do { /* must be at least one */
650: if (df_no_use_specs >= max_using)
651: int_error("Too many columns in using specification", c_token);
652:
653: if (equals(c_token, ":")) {
654: /* empty specification - use default */
655: use_spec[df_no_use_specs].column = df_no_use_specs;
656: ++df_no_use_specs;
657: /* do not increment c+token ; let while() find the : */
658: } else if (equals(c_token, "(")) {
659: fast_columns = 0; /* corey@cac */
660: dummy_func = NULL; /* no dummy variables active */
661: use_spec[df_no_use_specs++].at = perm_at(); /* it will match ()'s */
662: } else {
663: int col = (int) real(const_express(&a));
664: if (col < -2)
665: int_error("Column must be >= -2", c_token);
666: use_spec[df_no_use_specs++].column = col;
667: }
668: } while (equals(c_token, ":") && ++c_token);
669: }
670: if (!END_OF_COMMAND && isstring(c_token)) {
671: if (df_binary)
672: int_error("Format string meaningless with binary data", NO_CARET);
673:
674: quote_str(df_format, c_token, MAX_LINE_LEN);
675: if (!valid_format(df_format))
676: int_error("Please use a double conversion %lf", c_token);
677:
678: c_token++; /* skip format */
679: }
680: }
681: /*}}} */
682:
683: /*{{{ more variable inits */
684: point_count = -1; /* we preincrement */
685: line_count = 0;
686:
687: /* here so it's not done for every line in df_readline */
688: if (max_line_len < 160)
689: line = (char *) gp_alloc(max_line_len = 160, "datafile line buffer");
690:
691:
692: /*}}} */
693:
694:
695: /*{{{ open file */
696: #if defined(PIPES)
697: if (*filename == '<') {
698: if ((data_fp = popen(filename + 1, "r")) == (FILE *) NULL)
699: os_error("cannot create pipe for data", name_token);
700: else
701: pipe_open = TRUE;
702: } else
703: #endif /* PIPES */
704: if (*filename == '-') {
705: data_fp = lf_top();
706: if (!data_fp)
707: data_fp = stdin;
708: mixed_data_fp = TRUE; /* don't close command file */
709: } else {
710: char msg[MAX_LINE_LEN+1];
711: #ifdef HAVE_SYS_STAT_H
712: struct stat statbuf;
713:
714: if ((stat(filename, &statbuf) > -1) &&
715: !S_ISREG(statbuf.st_mode) && !S_ISFIFO(statbuf.st_mode)) {
716: sprintf(msg, "\"%s\" is not a regular file or pipe", filename);
717: os_error(msg, name_token);
718: }
719: #endif /* HAVE_SYS_STAT_H */
720: if ((data_fp = fopen(filename, df_binary ? "rb" : "r")) == (FILE *) NULL) {
721: /* one day we will have proper printf-style error reporting fns */
722: sprintf(msg, "can't read data file \"%s\"", filename);
723: os_error(msg, name_token);
724: }
725: }
726: /*}}} */
727:
728: return df_no_use_specs;
729: }
730: /*}}} */
731:
732: /*{{{ void df_close() */
733: void df_close()
734: {
735: int i;
736: /* paranoid - mark $n and column(n) as invalid */
737: df_no_cols = 0;
738:
739: if (!data_fp)
740: return;
741:
742: if (ydata_func.at) {
743: free(ydata_func.at);
744: ydata_func.at = NULL;
745: }
746: /*{{{ free any use expression storage */
747: for (i = 0; i < df_no_use_specs; ++i)
748: if (use_spec[i].at) {
749: free(use_spec[i].at);
750: use_spec[i].at = NULL;
751: }
752: /*}}} */
753:
754: if (!mixed_data_fp) {
755: #if defined(PIPES)
756: if (pipe_open) {
757: (void) pclose(data_fp);
758: pipe_open = FALSE;
759: } else
760: #endif /* PIPES */
761: (void) fclose(data_fp);
762: }
763: mixed_data_fp = FALSE;
764: data_fp = NULL;
765: }
766:
767: /*}}} */
768:
769: /*{{{ int df_readline(v, max) */
770: /* do the hard work... read lines from file,
771: * - use blanks to get index number
772: * - ignore lines outside range of indices required
773: * - fill v[] based on using spec if given
774: */
775:
776: int df_readline(v, max)
777: double v[];
778: int max;
779: {
780: char *s;
781:
782: assert(data_fp != NULL);
783: assert(!df_binary);
784: assert(max_line_len); /* alloc-ed in df_open() */
785: assert(max <= NCOL);
786:
787: /* catch attempt to read past EOF on mixed-input */
788: if (df_eof)
789: return DF_EOF;
790:
791: while ((s = df_gets()) != NULL)
792: /*{{{ process line */
793: {
794: int line_okay = 1;
795: int output = 0; /* how many numbers written to v[] */
796:
797: ++df_line_number;
798: df_no_cols = 0;
799:
800: /*{{{ check for blank lines, and reject by index/every */
801: /*{{{ skip leading spaces */
802: while (isspace((int)*s))
803: ++s; /* will skip the \n too, to point at \0 */
804: /*}}} */
805:
806: /*{{{ skip comments */
807: if (is_comment(*s))
808: continue; /* ignore comments */
809: /*}}} */
810:
811: /*{{{ check EOF on mixed data */
812: if (mixed_data_fp && is_EOF(*s)) {
813: df_eof = 1; /* trap attempts to read past EOF */
814: return DF_EOF;
815: }
816: /*}}} */
817:
818: /*{{{ its a blank line - update counters and continue or return */
819: if (*s == 0) {
820: /* argh - this is complicated ! we need to
821: * ignore it if we haven't reached first index
822: * report EOF if passed last index
823: * report blank line unless we've already done 2 blank lines
824: *
825: * - I have probably missed some obvious way of doing all this,
826: * but its getting late
827: */
828:
829: point_count = -1; /* restart counter within line */
830:
831: if (++blank_count == 1) {
832: /* first blank line */
833: ++line_count;
834: }
835:
836: /* just reached end of a group/surface */
837: if (blank_count == 2) {
838: ++df_current_index;
839: line_count = 0;
840: df_datum = -1;
841: /* ignore line if current_index has just become
842: * first required one - client doesn't want this
843: * blank line. While we're here, check for <=
844: * - we need to do it outside this conditional, but
845: * probably no extra cost at assembler level
846: */
847: if (df_current_index <= df_lower_index)
848: continue; /* dont tell client */
849:
850: /* df_upper_index is MAXINT-1 if we are not doing index */
851: if (df_current_index > df_upper_index) {
852: /* oops - need to gobble rest of input if mixed */
853: if (mixed_data_fp)
854: continue;
855: else {
856: df_eof = 1;
857: return DF_EOF; /* no point continuing */
858: }
859: }
860: }
861: /* dont tell client if we haven't reached first index */
862: if (df_current_index < df_lower_index)
863: continue;
864:
865: /* ignore blank lines after blank_index */
866: if (blank_count > 2)
867: continue;
868:
869: return DF_FIRST_BLANK - (blank_count - 1);
870: }
871: /*}}} */
872:
873: /* get here => was not blank */
874:
875: blank_count = 0;
876:
877: /*{{{ ignore points outside range of index */
878: /* we try to return end-of-file as soon as we pass upper index,
879: * but for mixed input stream, we must skip garbage
880: */
881:
882: if (df_current_index < df_lower_index ||
883: df_current_index > df_upper_index ||
884: ((df_current_index - df_lower_index) % df_index_step) != 0)
885: continue;
886: /*}}} */
887:
888: /*{{{ reject points by every */
889: /* accept only lines with (line_count%everyline) == 0 */
890:
891: if (line_count < firstline || line_count > lastline ||
892: (line_count - firstline) % everyline != 0
893: )
894: continue;
895:
896: /* update point_count. ignore point if point_count%everypoint != 0 */
897:
898: if (++point_count < firstpoint || point_count > lastpoint ||
899: (point_count - firstpoint) % everypoint != 0
900: )
901: continue;
902: /*}}} */
903: /*}}} */
904:
905: ++df_datum;
906:
907: /*{{{ do a sscanf */
908: if (*df_format) {
909: int i;
910:
911: assert(NCOL == 7);
912:
913: /* check we have room for at least 7 columns */
914: if (df_max_cols < 7)
915: df_column = (df_column_struct *) gp_realloc(df_column, (df_max_cols = 7) * sizeof(df_column_struct), "datafile columns");
916:
917: df_no_cols = sscanf(line, df_format,
918: &df_column[0].datum,
919: &df_column[1].datum,
920: &df_column[2].datum,
921: &df_column[3].datum,
922: &df_column[4].datum,
923: &df_column[5].datum,
924: &df_column[6].datum);
925:
926: if (df_no_cols == EOF) {
927: df_eof = 1;
928: return DF_EOF; /* tell client */
929: }
930: for (i = 0; i < df_no_cols; ++i) { /* may be zero */
931: df_column[i].good = DF_GOOD;
932: df_column[i].position = NULL; /* cant get a time */
933: }
934: }
935: /*}}} */
936: else
937: df_tokenise(s);
938:
939: /*{{{ copy column[] to v[] via use[] */
940: {
941: int limit = (df_no_use_specs ? df_no_use_specs : NCOL);
942: if (limit > max)
943: limit = max;
944:
945: for (output = 0; output < limit; ++output) {
946: /* if there was no using spec, column is output+1 and at=NULL */
947: int column = use_spec[output].column;
948:
949: if (use_spec[output].at) {
950: struct value a;
951: /* no dummy values to set up prior to... */
952: evaluate_at(use_spec[output].at, &a);
953: if (undefined)
954: return DF_UNDEFINED; /* store undefined point in plot */
955:
956: v[output] = real(&a);
957: } else if (column == -2) {
958: v[output] = df_current_index;
959: } else if (column == -1) {
960: v[output] = line_count;
961: } else if (column == 0) {
962: v[output] = df_datum; /* using 0 */
963: } else if (column <= 0) /* really < -2, but */
964: int_error("internal error: column <= 0 in datafile.c", NO_CARET);
965: else if (df_timecol[output]) {
966: struct tm tm;
967: if (column > df_no_cols ||
968: df_column[column - 1].good == DF_MISSING ||
969: !df_column[column - 1].position ||
970: !gstrptime(df_column[column - 1].position, timefmt, &tm)
971: ) {
972: /* line bad only if user explicitly asked for this column */
973: if (df_no_use_specs)
974: line_okay = 0;
975:
976: /* return or ignore line depending on line_okay */
977: break;
978: }
979: v[output] = (double) gtimegm(&tm);
980: } else { /* column > 0 */
981: if ((column <= df_no_cols) && df_column[column - 1].good == DF_GOOD)
982: v[output] = df_column[column - 1].datum;
983: else {
984: /* line bad only if user explicitly asked for this column */
985: if (df_no_use_specs)
986: line_okay = 0;
987: break; /* return or ignore depending on line_okay */
988: }
989: }
990: }
991: }
992: /*}}} */
993:
994: if (!line_okay)
995: continue;
996:
997: /* output == df_no_use_specs if using was specified
998: * - actually, smaller of df_no_use_specs and max
999: */
1000: assert(df_no_use_specs == 0 || output == df_no_use_specs || output == max);
1001:
1002: return output;
1003:
1004: }
1005: /*}}} */
1006:
1007: /* get here => fgets failed */
1008:
1009: /* no longer needed - mark column(x) as invalid */
1010: df_no_cols = 0;
1011:
1012: df_eof = 1;
1013: return DF_EOF;
1014: }
1015: /*}}} */
1016:
1017: /*{{{ int df_2dbinary(this_plot) */
1018: int df_2dbinary(this_plot)
1019: struct curve_points *this_plot;
1020: {
1021: int_error("Binary file format for 2d data not yet defined", NO_CARET);
1022: return 0; /* keep compiler happy */
1023: }
1024: /*}}} */
1025:
1026: /*{{{ int df_3dmatrix(this_plot, ret_this_iso) */
1027: /*
1028: * formerly in gnubin.c
1029: *
1030: * modified by div for 3.6
1031: * obey the 'every' field from df_open
1032: * outrange points are marked as such, not omitted
1033: * obey using - treat x as column 1, y as col 2 and z as col 3
1034: * ( ie $1 gets x, $2 gets y, $3 gets z)
1035: *
1036: * we are less optimal for case of log plot and no using spec,
1037: * (call log too often) but that is price for flexibility
1038: * I suspect it didn't do autoscaling of x and y for log scale
1039: * properly ?
1040: *
1041: * Trouble figuring out file format ! Is it
1042:
1043: width x1 x2 x3 x4 x5 ...
1044: y1 z11 z12 z13 z14 z15 ...
1045: y2 x21 z22 z23 .....
1046: . .
1047: . .
1048: . .
1049:
1050: * with perhaps x and y swapped...
1051: *
1052: * - presumably rows continue to end of file, hence no indexing...
1053: *
1054: * Last update: 3/3/92 for Gnuplot 3.24.
1055: * Created from code for written by RKC for gnuplot 2.0b.
1056: *
1057: * 19 September 1992 Lawrence Crowl (crowl@cs.orst.edu)
1058: * Added user-specified bases for log scaling.
1059: *
1060: * Copyright (c) 1991,1992 Robert K. Cunningham, MIT Lincoln Laboratory
1061: *
1062: */
1063:
1064: /*
1065: Here we keep putting new plots onto the end of the linked list
1066:
1067: We assume the data's x,y values have x1<x2, x2<x3... and
1068: y1<y2, y2<y3... .
1069: Actually, I think the assumption is less strong than that--it looks like
1070: the direction just has to be the same.
1071: This routine expects the following to be properly initialized:
1072: is_log_x, is_log_y, and is_log_z
1073: base_log_x, base_log_y, and base_log_z
1074: log_base_log_x, log_base_log_y, and log_base_log_z
1075: xmin,ymin, and zmin
1076: xmax,ymax, and zmax
1077: autoscale_lx, autoscale_ly, and autoscale_lz
1078:
1079: does the autoscaling into the array versions (min_array[], max_array[])
1080: */
1081:
1082: int df_3dmatrix(this_plot)
1083: struct surface_points *this_plot;
1084: {
1085: float GPFAR *GPFAR * dmatrix, GPFAR * rt, GPFAR * ct;
1086: int nr, nc;
1087: int width, height;
1088: int row, col;
1089: struct iso_curve *this_iso;
1090: double used[3]; /* output from using manip */
1091: struct coordinate GPHUGE *point; /* HBB 980308: added 'GPHUGE' flag */
1092:
1093: assert(df_matrix);
1094:
1095: if (df_eof)
1096: return 0; /* hope caller understands this */
1097:
1098: if (df_binary) {
1099: if (!fread_matrix(data_fp, &dmatrix, &nr, &nc, &rt, &ct))
1100: int_error("Binary file read error: format unknown!", NO_CARET);
1101: /* fread_matrix() drains the file */
1102: df_eof = 1;
1103: } else {
1104: if (!(dmatrix = df_read_matrix(&nr, &nc))) {
1105: df_eof = 1;
1106: return 0;
1107: }
1108: rt = NULL;
1109: ct = NULL;
1110: }
1111:
1112: if (nc == 0 || nr == 0)
1113: int_error("Read grid of zero height or zero width", NO_CARET);
1114:
1115: this_plot->plot_type = DATA3D;
1116: this_plot->has_grid_topology = TRUE;
1117:
1118: if (df_no_use_specs != 0 && df_no_use_specs != 3)
1119: int_error("Current implementation requires full using spec", NO_CARET);
1120:
1121: if (df_max_cols < 3 &&
1122: !(df_column = (df_column_struct *) gp_realloc(df_column, (df_max_cols = 3) * sizeof(df_column_struct), "datafile columns"))
1123: )
1124: int_error("Out of store in binary read", c_token);
1125:
1126: df_no_cols = 3;
1127: df_column[0].good = df_column[1].good = df_column[2].good = DF_GOOD;
1128:
1129: assert(everyline > 0);
1130: assert(everypoint > 0);
1131: width = (nc - firstpoint + everypoint - 1) / everypoint; /* ? ? ? ? ? */
1132: height = (nr - firstline + everyline - 1) / everyline; /* ? ? ? ? ? */
1133:
1134: for (row = firstline; row < nr; row += everyline) {
1135: df_column[1].datum = rt ? rt[row] : row;
1136:
1137: /*Allocate the correct number of entries */
1138: this_iso = iso_alloc(width);
1139:
1140: point = this_iso->points;
1141:
1142: /* Cycle through data */
1143: for (col = firstpoint; col < nc; col += everypoint, ++point) {
1144: /*{{{ process one point */
1145: int i;
1146:
1147: df_column[0].datum = ct ? ct[col] : col;
1148: df_column[2].datum = dmatrix[row][col];
1149:
1150: /*{{{ pass through using spec */
1151: for (i = 0; i < 3; ++i) {
1152: int column = use_spec[i].column;
1153:
1154: if (df_no_use_specs == 0)
1155: used[i] = df_column[i].datum;
1156: else if (use_spec[i].at) {
1157: struct value a;
1158: evaluate_at(use_spec[i].at, &a);
1159: if (undefined) {
1160: point->type = UNDEFINED;
1161: goto skip; /* continue _outer_ loop */
1162: }
1163: used[i] = real(&a);
1164: } else if (column < 1 || column > df_no_cols) {
1165: point->type = UNDEFINED;
1166: goto skip;
1167: } else
1168: used[i] = df_column[column - 1].datum;
1169: }
1170: /*}}} */
1171:
1172: point->type = INRANGE; /* so far */
1173:
1174: /*{{{ autoscaling/clipping */
1175: /*{{{ autoscale/range-check x */
1176: if (used[0] > 0 || !is_log_x) {
1177: if (used[0] < min_array[FIRST_X_AXIS]) {
1178: if (autoscale_lx & 1)
1179: min_array[FIRST_X_AXIS] = used[0];
1180: else
1181: point->type = OUTRANGE;
1182: }
1183: if (used[0] > max_array[FIRST_X_AXIS]) {
1184: if (autoscale_lx & 2)
1185: max_array[FIRST_X_AXIS] = used[0];
1186: else
1187: point->type = OUTRANGE;
1188: }
1189: }
1190: /*}}} */
1191:
1192: /*{{{ autoscale/range-check y */
1193: if (used[1] > 0 || !is_log_y) {
1194: if (used[1] < min_array[FIRST_Y_AXIS]) {
1195: if (autoscale_ly & 1)
1196: min_array[FIRST_Y_AXIS] = used[1];
1197: else
1198: point->type = OUTRANGE;
1199: }
1200: if (used[1] > max_array[FIRST_Y_AXIS]) {
1201: if (autoscale_ly & 2)
1202: max_array[FIRST_Y_AXIS] = used[1];
1203: else
1204: point->type = OUTRANGE;
1205: }
1206: }
1207: /*}}} */
1208:
1209: /*{{{ autoscale/range-check z */
1210: if (used[2] > 0 || !is_log_z) {
1211: if (used[2] < min_array[FIRST_Z_AXIS]) {
1212: if (autoscale_lz & 1)
1213: min_array[FIRST_Z_AXIS] = used[2];
1214: else
1215: point->type = OUTRANGE;
1216: }
1217: if (used[2] > max_array[FIRST_Z_AXIS]) {
1218: if (autoscale_lz & 2)
1219: max_array[FIRST_Z_AXIS] = used[2];
1220: else
1221: point->type = OUTRANGE;
1222: }
1223: }
1224: /*}}} */
1225: /*}}} */
1226:
1227: /*{{{ log x */
1228: if (is_log_x) {
1229: if (used[0] < 0.0) {
1230: point->type = UNDEFINED;
1231: goto skip;
1232: } else if (used[0] == 0.0) {
1233: point->type = OUTRANGE;
1234: used[0] = -VERYLARGE;
1235: } else
1236: used[0] = log(used[0]) / log_base_log_x;
1237: }
1238: /*}}} */
1239:
1240: /*{{{ log y */
1241: if (is_log_y) {
1242: if (used[1] < 0.0) {
1243: point->type = UNDEFINED;
1244: goto skip;
1245: } else if (used[1] == 0.0) {
1246: point->type = OUTRANGE;
1247: used[1] = -VERYLARGE;
1248: } else
1249: used[1] = log(used[1]) / log_base_log_y;
1250: }
1251: /*}}} */
1252:
1253: /*{{{ log z */
1254: if (is_log_z) {
1255: if (used[2] < 0.0) {
1256: point->type = UNDEFINED;
1257: goto skip;
1258: } else if (used[2] == 0.0) {
1259: point->type = OUTRANGE;
1260: used[2] = -VERYLARGE;
1261: } else
1262: used[2] = log(used[2]) / log_base_log_z;
1263: }
1264: /*}}} */
1265:
1266: point->x = used[0];
1267: point->y = used[1];
1268: point->z = used[2];
1269:
1270:
1271: /* some of you wont like this, but I say goto is for this */
1272:
1273: skip:
1274: ; /* ansi requires this */
1275: /*}}} */
1276: }
1277: this_iso->p_count = width;
1278: this_iso->next = this_plot->iso_crvs;
1279: this_plot->iso_crvs = this_iso;
1280: this_plot->num_iso_read++;
1281: }
1282:
1283: free_matrix(dmatrix, 0, nr - 1, 0, nc - 1);
1284: if (rt)
1285: free_vector(rt, 0, nr - 1);
1286: if (ct)
1287: free_vector(ct, 0, nc - 1);
1288: return (nc);
1289: }
1290: /*}}} */
1291:
1292: /* stuff for implementing the call-backs for picking up data values
1293: * do it here so we can make the variables private to this file
1294: */
1295:
1296: /*{{{ void f_dollars(x) */
1297: void f_dollars(x)
1298: union argument *x;
1299: {
1300: int column = x->v_arg.v.int_val - 1;
1301: /* we checked it was an integer >= 0 at compile time */
1302: struct value a;
1303:
1304: if (column == -1) {
1305: push(Gcomplex(&a, (double) df_datum, 0.0)); /* $0 */
1306: } else if (column >= df_no_cols || df_column[column].good != DF_GOOD) {
1307: undefined = TRUE;
1308: push(&(x->v_arg)); /* this okay ? */
1309: } else
1310: push(Gcomplex(&a, df_column[column].datum, 0.0));
1311: }
1312: /*}}} */
1313:
1314: /*{{{ void f_column() */
1315: void f_column()
1316: {
1317: struct value a;
1318: int column;
1319: (void) pop(&a);
1320: column = (int) real(&a) - 1;
1321: if (column == -2)
1322: push(Ginteger(&a, line_count));
1323: else if (column == -1) /* $0 = df_datum */
1324: push(Gcomplex(&a, (double) df_datum, 0.0));
1325: else if (column < 0 || column >= df_no_cols || df_column[column].good != DF_GOOD) {
1326: undefined = TRUE;
1327: push(&a); /* any objection to this ? */
1328: } else
1329: push(Gcomplex(&a, df_column[column].datum, 0.0));
1330: }
1331: /*}}} */
1332:
1333: /*{{{ void f_valid() */
1334: void f_valid()
1335: {
1336: struct value a;
1337: int column, good;
1338: (void) pop(&a);
1339: column = (int) magnitude(&a) - 1;
1340: good = column >= 0 && column < df_no_cols && df_column[column].good == DF_GOOD;
1341: push(Ginteger(&a, good));
1342: }
1343:
1344: /*}}} */
1345:
1346: /*{{{ void f_timecolumn() */
1347: void f_timecolumn()
1348: {
1349: struct value a;
1350: int column;
1351: struct tm tm;
1352: (void) pop(&a);
1353: column = (int) magnitude(&a) - 1;
1354: if (column < 0 || column >= df_no_cols ||
1355: !df_column[column].position ||
1356: !gstrptime(df_column[column].position, timefmt, &tm)
1357: ) {
1358: undefined = TRUE;
1359: push(&a); /* any objection to this ? */
1360: } else
1361: push(Gcomplex(&a, gtimegm(&tm), 0.0));
1362: }
1363: /*}}} */
1364:
1365: #if 0 /* not used */
1366: /* count columns in timefmt */
1367: /*{{{ static int get_time_cols(fmt) */
1368: static int get_time_cols(fmt)
1369: char *fmt; /* format string */
1370: {
1371: int cnt, i;
1372: char *p;
1373:
1374: p = fmt;
1375: cnt = 0;
1376: while (isspace(*p))
1377: p++;
1378: if (!strlen(p))
1379: int_error("Empty time-data format", NO_CARET);
1380: cnt++;
1381: for (i = 0; i < strlen(p) - 1; i++) {
1382: if (isspace(p[i]) && !isspace(p[i + 1]))
1383: cnt++;
1384: }
1385: return (cnt);
1386: }
1387: /*}}} */
1388:
1389: /* modify default use_spec, applies for no user spec and time datacolumns */
1390: /*{{{ static void mod_def_usespec(specno,jump) */
1391: static void mod_def_usespec(specno, jump)
1392: int specno; /* which spec in ?:?:? */
1393: int jump; /* no of columns in timefmt (time data) */
1394: {
1395: int i;
1396:
1397: for (i = specno + 1; i < NCOL; ++i)
1398: use_spec[i].column += jump; /* add no of columns in time to the rest */
1399: df_no_use_specs = 0;
1400: }
1401: /*}}} */
1402: #endif /* not used */
1403:
1404: /*{{{ static int check_missing(s) */
1405: static int check_missing(s)
1406: char *s;
1407: {
1408: if (missing_val != NULL) {
1409: int len = strlen(missing_val);
1410: if (strncmp(s, missing_val, len) == 0 &&
1411: (isspace((int)s[len]) || !s[len])) {
1412: return (1);; /* store undefined point in plot */
1413: }
1414: }
1415: return (0);
1416: }
1417: /*}}} */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>