Annotation of OpenXM_contrib/gnuplot/datafile.c, Revision 1.1.1.1
1.1 maekawa 1: #ifndef lint
2: static char *RCSid = "$Id: datafile.c,v 1.42 1998/04/14 00:15:17 drd Exp $";
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 */
351: while (isspace((int)*s))
352: ++s;
353: count = *s ? 1 : 0;
354: /* skip chars to end of column */
355: for (used = 0; !isspace((int)*s) && (*s != NUL); ++used, ++s)
356: ;
357: }
358:
359: /* it might be a fortran double or quad precision.
360: * 'used' is only safe if count is 1
361: */
362:
363: #ifndef NO_FORTRAN_NUMS
364: if (count == 1 &&
365: (s[used] == 'd' || s[used] == 'D' ||
366: s[used] == 'q' || s[used] == 'Q')) {
367: /* might be fortran double */
368: s[used] = 'e';
369: /* and try again */
370: count = sscanf(s, "%lf", &df_column[df_no_cols].datum);
371: }
372: #endif /* NO_FORTRAN_NUMS */
373: #endif /* OSK */
374: df_column[df_no_cols].good = count == 1 ? DF_GOOD : DF_BAD;
375: }
376:
377: ++df_no_cols;
378: /*{{{ skip chars to end of column */
379: while ((!isspace((int)*s)) && (*s != '\0'))
380: ++s;
381: /*}}} */
382: /*{{{ skip spaces to start of next column */
383: while (isspace((int)*s))
384: ++s;
385: /*}}} */
386: }
387:
388: return df_no_cols;
389: }
390: /*}}} */
391:
392: /*{{{ static float **df_read_matrix() */
393: /* reads a matrix from a text file
394: * stores in same storage format as fread_matrix
395: */
396:
397: static float **df_read_matrix(rows, cols)
398: int *rows, *cols;
399: {
400: int max_rows = 0;
401: int c;
402: float **rmatrix = NULL;
403:
404: char *s;
405:
406: *rows = 0;
407: *cols = 0;
408:
409: for (;;) {
410: if (!(s = df_gets())) {
411: df_eof = 1;
412: return rmatrix; /* NULL if we have not read anything yet */
413: }
414: while (isspace((int)*s))
415: ++s;
416:
417: if (!*s || is_comment(*s)) {
418: if (rmatrix)
419: return rmatrix;
420: else
421: continue;
422: }
423: if (mixed_data_fp && is_EOF(*s)) {
424: df_eof = 1;
425: return rmatrix;
426: }
427: c = df_tokenise(s);
428:
429: if (!c)
430: return rmatrix;
431:
432: if (*cols && c != *cols) {
433: /* its not regular */
434: int_error("Matrix does not represent a grid", NO_CARET);
435: }
436: *cols = c;
437:
438: if (*rows >= max_rows) {
439: rmatrix = gp_realloc(rmatrix, (max_rows += 10) * sizeof(float *), "df_matrix");
440: }
441: /* allocate a row and store data */
442: {
443: int i;
444: float *row = rmatrix[*rows] = (float *) gp_alloc(c * sizeof(float), "df_matrix row");
445:
446: for (i = 0; i < c; ++i) {
447: if (df_column[i].good != DF_GOOD)
448: int_error("Bad number in matrix", NO_CARET);
449:
450: row[i] = (float) df_column[i].datum;
451: }
452:
453: ++*rows;
454: }
455: }
456: }
457:
458: /*}}} */
459:
460:
461: /*{{{ int df_open(max_using) */
462: int df_open(max_using)
463: int max_using;
464:
465: /* open file, parsing using/thru/index stuff
466: * return number of using specs [well, we have to return something !]
467: */
468:
469: {
470: static char filename[MAX_LINE_LEN + 1] = "";
471: int i;
472: int name_token;
473:
474: fast_columns = 1; /* corey@cac */
475:
476: /*{{{ close file if necessary */
477: if (data_fp)
478: df_close();
479: /*}}} */
480:
481: /*{{{ initialise static variables */
482: df_format[0] = NUL; /* no format string */
483:
484: df_no_use_specs = 0;
485:
486: for (i = 0; i < NCOL; ++i) {
487: use_spec[i].column = i + 1; /* default column */
488: use_spec[i].at = NULL; /* no expression */
489: }
490:
491: if (max_using > NCOL)
492: max_using = NCOL;
493:
494: df_datum = -1; /* it will be preincremented before use */
495: df_line_number = 0; /* ditto */
496:
497: df_lower_index = 0;
498: df_index_step = 1;
499: df_upper_index = MAXINT;
500:
501: df_current_index = 0;
502: blank_count = 2;
503: /* by initialising blank_count, leading blanks will be ignored */
504:
505: everypoint = everyline = 1; /* unless there is an every spec */
506: firstpoint = firstline = 0;
507: lastpoint = lastline = MAXINT;
508:
509: df_eof = 0;
510:
511: memset(df_timecol, 0, sizeof(df_timecol));
512:
513: df_binary = 1;
514: /*}}} */
515:
516: assert(max_using <= NCOL);
517:
518: /* empty name means re-use last one */
519:
520: {
521: char name[MAX_LINE_LEN + 1];
522: quote_str(name, c_token, MAX_LINE_LEN);
523: if (name[0])
524: strcpy(filename, name);
525: else if (!filename[0])
526: int_error("No previous filename", c_token);
527: }
528: name_token = c_token++;
529:
530: /* defer opening until we have parsed the modifiers... */
531:
532: /*{{{ look for binary / matrix */
533: df_binary = df_matrix = FALSE;
534:
535: if (almost_equals(c_token, "bin$ary")) {
536: ++c_token;
537: df_binary = TRUE;
538: df_matrix = TRUE;
539: } else if (almost_equals(c_token, "mat$rix")) {
540: ++c_token;
541: df_matrix = TRUE;
542: }
543: /*}}} */
544:
545: /*{{{ deal with index */
546: if (almost_equals(c_token, "i$ndex")) {
547: struct value a;
548:
549: if (df_binary)
550: int_error("Binary file format does not allow more than one surface per file", c_token);
551:
552: ++c_token;
553: df_lower_index = (int) real(const_express(&a));
554: if (equals(c_token, ":")) {
555: ++c_token;
556: df_upper_index = (int) magnitude(const_express(&a));
557: if (df_upper_index < df_lower_index)
558: int_error("Upper index should be bigger than lower index", c_token);
559:
560: if (equals(c_token, ":")) {
561: ++c_token;
562: df_index_step = (int) magnitude(const_express(&a));
563: if (df_index_step < 1)
564: int_error("Index step must be positive", c_token);
565: }
566: } else
567: df_upper_index = df_lower_index;
568: }
569: /*}}} */
570:
571: /*{{{ deal with every */
572: if (almost_equals(c_token, "ev$ery")) {
573: struct value a;
574:
575: fast_columns = 0; /* corey@cac */
576: /* allow empty fields - every a:b:c::e
577: * we have already established the defaults
578: */
579:
580: if (!equals(++c_token, ":")) {
581: everypoint = (int) real(const_express(&a));
582: if (everypoint < 1)
583: int_error("Expected positive integer", c_token);
584: }
585: /* if it fails on first test, no more tests will succeed. If it
586: * fails on second test, next test will succeed with correct c_token
587: */
588: if (equals(c_token, ":") && !equals(++c_token, ":")) {
589: everyline = (int) real(const_express(&a));
590: if (everyline < 1)
591: int_error("Expected positive integer", c_token);
592: }
593: if (equals(c_token, ":") && !equals(++c_token, ":")) {
594: firstpoint = (int) real(const_express(&a));
595: if (firstpoint < 0)
596: int_error("Expected non-negative integer", c_token);
597: }
598: if (equals(c_token, ":") && !equals(++c_token, ":")) {
599: firstline = (int) real(const_express(&a));
600: if (firstline < 0)
601: int_error("Expected non-negative integer", c_token);
602: }
603: if (equals(c_token, ":") && !equals(++c_token, ":")) {
604: lastpoint = (int) real(const_express(&a));
605: if (lastpoint < firstpoint)
606: int_error("Last point must not be before first point", c_token);
607: }
608: if (equals(c_token, ":")) {
609: ++c_token;
610: lastline = (int) real(const_express(&a));
611: if (lastline < firstline)
612: int_error("Last line must not be before first line", c_token);
613: }
614: }
615: /*}}} */
616:
617: /*{{{ deal with thru */
618: /* jev -- support for passing data from file thru user function */
619:
620: if (almost_equals(c_token, "thru$")) {
621: c_token++;
622: if (ydata_func.at)
623: free(ydata_func.at);
624: strcpy(c_dummy_var[0], dummy_var[0]);
625: /* allow y also as a dummy variable.
626: * during plot, c_dummy_var[0] and [1] are 'sacred'
627: * ie may be set by splot [u=1:2] [v=1:2], and these
628: * names are stored only in c_dummy_var[]
629: * so choose dummy var 2 - can anything vital be here ?
630: */
631: dummy_func = &ydata_func;
632: strcpy(c_dummy_var[2], "y");
633: ydata_func.at = perm_at();
634: dummy_func = NULL;
635: } else {
636: if (ydata_func.at)
637: free(ydata_func.at);
638: ydata_func.at = NULL;
639: }
640: /*}}} */
641:
642: /*{{{ deal with using */
643: if (almost_equals(c_token, "u$sing")) {
644: if (!END_OF_COMMAND && !isstring(++c_token)) {
645: struct value a;
646:
647: do { /* must be at least one */
648: if (df_no_use_specs >= max_using)
649: int_error("Too many columns in using specification", c_token);
650:
651: if (equals(c_token, ":")) {
652: /* empty specification - use default */
653: use_spec[df_no_use_specs].column = df_no_use_specs;
654: ++df_no_use_specs;
655: /* do not increment c+token ; let while() find the : */
656: } else if (equals(c_token, "(")) {
657: fast_columns = 0; /* corey@cac */
658: dummy_func = NULL; /* no dummy variables active */
659: use_spec[df_no_use_specs++].at = perm_at(); /* it will match ()'s */
660: } else {
661: int col = (int) real(const_express(&a));
662: if (col < -2)
663: int_error("Column must be >= -2", c_token);
664: use_spec[df_no_use_specs++].column = col;
665: }
666: } while (equals(c_token, ":") && ++c_token);
667: }
668: if (!END_OF_COMMAND && isstring(c_token)) {
669: if (df_binary)
670: int_error("Format string meaningless with binary data", NO_CARET);
671:
672: quote_str(df_format, c_token, MAX_LINE_LEN);
673: if (!valid_format(df_format))
674: int_error("Please use a double conversion %lf", c_token);
675:
676: c_token++; /* skip format */
677: }
678: }
679: /*}}} */
680:
681: /*{{{ more variable inits */
682: point_count = -1; /* we preincrement */
683: line_count = 0;
684:
685: /* here so it's not done for every line in df_readline */
686: if (max_line_len < 160)
687: line = (char *) gp_alloc(max_line_len = 160, "datafile line buffer");
688:
689:
690: /*}}} */
691:
692:
693: /*{{{ open file */
694: #if defined(PIPES)
695: if (*filename == '<') {
696: if ((data_fp = popen(filename + 1, "r")) == (FILE *) NULL)
697: os_error("cannot create pipe for data", name_token);
698: else
699: pipe_open = TRUE;
700: } else
701: #endif /* PIPES */
702: if (*filename == '-') {
703: data_fp = lf_top();
704: if (!data_fp)
705: data_fp = stdin;
706: mixed_data_fp = TRUE; /* don't close command file */
707: } else {
708: char msg[MAX_LINE_LEN+1];
709: #ifdef HAVE_SYS_STAT_H
710: struct stat statbuf;
711:
712: if ((stat(filename, &statbuf) > -1) &&
713: !S_ISREG(statbuf.st_mode) && !S_ISFIFO(statbuf.st_mode)) {
714: sprintf(msg, "\"%s\" is not a regular file or pipe", filename);
715: os_error(msg, name_token);
716: }
717: #endif /* HAVE_SYS_STAT_H */
718: if ((data_fp = fopen(filename, df_binary ? "rb" : "r")) == (FILE *) NULL) {
719: /* one day we will have proper printf-style error reporting fns */
720: sprintf(msg, "can't read data file \"%s\"", filename);
721: os_error(msg, name_token);
722: }
723: }
724: /*}}} */
725:
726: return df_no_use_specs;
727: }
728: /*}}} */
729:
730: /*{{{ void df_close() */
731: void df_close()
732: {
733: int i;
734: /* paranoid - mark $n and column(n) as invalid */
735: df_no_cols = 0;
736:
737: if (!data_fp)
738: return;
739:
740: if (ydata_func.at) {
741: free(ydata_func.at);
742: ydata_func.at = NULL;
743: }
744: /*{{{ free any use expression storage */
745: for (i = 0; i < df_no_use_specs; ++i)
746: if (use_spec[i].at) {
747: free(use_spec[i].at);
748: use_spec[i].at = NULL;
749: }
750: /*}}} */
751:
752: if (!mixed_data_fp) {
753: #if defined(PIPES)
754: if (pipe_open) {
755: (void) pclose(data_fp);
756: pipe_open = FALSE;
757: } else
758: #endif /* PIPES */
759: (void) fclose(data_fp);
760: }
761: mixed_data_fp = FALSE;
762: data_fp = NULL;
763: }
764:
765: /*}}} */
766:
767: /*{{{ int df_readline(v, max) */
768: /* do the hard work... read lines from file,
769: * - use blanks to get index number
770: * - ignore lines outside range of indices required
771: * - fill v[] based on using spec if given
772: */
773:
774: int df_readline(v, max)
775: double v[];
776: int max;
777: {
778: char *s;
779:
780: assert(data_fp != NULL);
781: assert(!df_binary);
782: assert(max_line_len); /* alloc-ed in df_open() */
783: assert(max <= NCOL);
784:
785: /* catch attempt to read past EOF on mixed-input */
786: if (df_eof)
787: return DF_EOF;
788:
789: while ((s = df_gets()) != NULL)
790: /*{{{ process line */
791: {
792: int line_okay = 1;
793: int output = 0; /* how many numbers written to v[] */
794:
795: ++df_line_number;
796: df_no_cols = 0;
797:
798: /*{{{ check for blank lines, and reject by index/every */
799: /*{{{ skip leading spaces */
800: while (isspace((int)*s))
801: ++s; /* will skip the \n too, to point at \0 */
802: /*}}} */
803:
804: /*{{{ skip comments */
805: if (is_comment(*s))
806: continue; /* ignore comments */
807: /*}}} */
808:
809: /*{{{ check EOF on mixed data */
810: if (mixed_data_fp && is_EOF(*s)) {
811: df_eof = 1; /* trap attempts to read past EOF */
812: return DF_EOF;
813: }
814: /*}}} */
815:
816: /*{{{ its a blank line - update counters and continue or return */
817: if (*s == 0) {
818: /* argh - this is complicated ! we need to
819: * ignore it if we haven't reached first index
820: * report EOF if passed last index
821: * report blank line unless we've already done 2 blank lines
822: *
823: * - I have probably missed some obvious way of doing all this,
824: * but its getting late
825: */
826:
827: point_count = -1; /* restart counter within line */
828:
829: if (++blank_count == 1) {
830: /* first blank line */
831: ++line_count;
832: }
833:
834: /* just reached end of a group/surface */
835: if (blank_count == 2) {
836: ++df_current_index;
837: line_count = 0;
838: df_datum = -1;
839: /* ignore line if current_index has just become
840: * first required one - client doesn't want this
841: * blank line. While we're here, check for <=
842: * - we need to do it outside this conditional, but
843: * probably no extra cost at assembler level
844: */
845: if (df_current_index <= df_lower_index)
846: continue; /* dont tell client */
847:
848: /* df_upper_index is MAXINT-1 if we are not doing index */
849: if (df_current_index > df_upper_index) {
850: /* oops - need to gobble rest of input if mixed */
851: if (mixed_data_fp)
852: continue;
853: else {
854: df_eof = 1;
855: return DF_EOF; /* no point continuing */
856: }
857: }
858: }
859: /* dont tell client if we haven't reached first index */
860: if (df_current_index < df_lower_index)
861: continue;
862:
863: /* ignore blank lines after blank_index */
864: if (blank_count > 2)
865: continue;
866:
867: return DF_FIRST_BLANK - (blank_count - 1);
868: }
869: /*}}} */
870:
871: /* get here => was not blank */
872:
873: blank_count = 0;
874:
875: /*{{{ ignore points outside range of index */
876: /* we try to return end-of-file as soon as we pass upper index,
877: * but for mixed input stream, we must skip garbage
878: */
879:
880: if (df_current_index < df_lower_index ||
881: df_current_index > df_upper_index ||
882: ((df_current_index - df_lower_index) % df_index_step) != 0)
883: continue;
884: /*}}} */
885:
886: /*{{{ reject points by every */
887: /* accept only lines with (line_count%everyline) == 0 */
888:
889: if (line_count < firstline || line_count > lastline ||
890: (line_count - firstline) % everyline != 0
891: )
892: continue;
893:
894: /* update point_count. ignore point if point_count%everypoint != 0 */
895:
896: if (++point_count < firstpoint || point_count > lastpoint ||
897: (point_count - firstpoint) % everypoint != 0
898: )
899: continue;
900: /*}}} */
901: /*}}} */
902:
903: ++df_datum;
904:
905: /*{{{ do a sscanf */
906: if (*df_format) {
907: int i;
908:
909: assert(NCOL == 7);
910:
911: /* check we have room for at least 7 columns */
912: if (df_max_cols < 7)
913: df_column = (df_column_struct *) gp_realloc(df_column, (df_max_cols = 7) * sizeof(df_column_struct), "datafile columns");
914:
915: df_no_cols = sscanf(line, df_format,
916: &df_column[0].datum,
917: &df_column[1].datum,
918: &df_column[2].datum,
919: &df_column[3].datum,
920: &df_column[4].datum,
921: &df_column[5].datum,
922: &df_column[6].datum);
923:
924: if (df_no_cols == EOF) {
925: df_eof = 1;
926: return DF_EOF; /* tell client */
927: }
928: for (i = 0; i < df_no_cols; ++i) { /* may be zero */
929: df_column[i].good = DF_GOOD;
930: df_column[i].position = NULL; /* cant get a time */
931: }
932: }
933: /*}}} */
934: else
935: df_tokenise(s);
936:
937: /*{{{ copy column[] to v[] via use[] */
938: {
939: int limit = (df_no_use_specs ? df_no_use_specs : NCOL);
940: if (limit > max)
941: limit = max;
942:
943: for (output = 0; output < limit; ++output) {
944: /* if there was no using spec, column is output+1 and at=NULL */
945: int column = use_spec[output].column;
946:
947: if (use_spec[output].at) {
948: struct value a;
949: /* no dummy values to set up prior to... */
950: evaluate_at(use_spec[output].at, &a);
951: if (undefined)
952: return DF_UNDEFINED; /* store undefined point in plot */
953:
954: v[output] = real(&a);
955: } else if (column == -2) {
956: v[output] = df_current_index;
957: } else if (column == -1) {
958: v[output] = line_count;
959: } else if (column == 0) {
960: v[output] = df_datum; /* using 0 */
961: } else if (column <= 0) /* really < -2, but */
962: int_error("internal error: column <= 0 in datafile.c", NO_CARET);
963: else if (df_timecol[output]) {
964: struct tm tm;
965: if (column > df_no_cols ||
966: df_column[column - 1].good == DF_MISSING ||
967: !df_column[column - 1].position ||
968: !gstrptime(df_column[column - 1].position, timefmt, &tm)
969: ) {
970: /* line bad only if user explicitly asked for this column */
971: if (df_no_use_specs)
972: line_okay = 0;
973:
974: /* return or ignore line depending on line_okay */
975: break;
976: }
977: v[output] = (double) gtimegm(&tm);
978: } else { /* column > 0 */
979: if ((column <= df_no_cols) && df_column[column - 1].good == DF_GOOD)
980: v[output] = df_column[column - 1].datum;
981: else {
982: /* line bad only if user explicitly asked for this column */
983: if (df_no_use_specs)
984: line_okay = 0;
985: break; /* return or ignore depending on line_okay */
986: }
987: }
988: }
989: }
990: /*}}} */
991:
992: if (!line_okay)
993: continue;
994:
995: /* output == df_no_use_specs if using was specified
996: * - actually, smaller of df_no_use_specs and max
997: */
998: assert(df_no_use_specs == 0 || output == df_no_use_specs || output == max);
999:
1000: return output;
1001:
1002: }
1003: /*}}} */
1004:
1005: /* get here => fgets failed */
1006:
1007: /* no longer needed - mark column(x) as invalid */
1008: df_no_cols = 0;
1009:
1010: df_eof = 1;
1011: return DF_EOF;
1012: }
1013: /*}}} */
1014:
1015: /*{{{ int df_2dbinary(this_plot) */
1016: int df_2dbinary(this_plot)
1017: struct curve_points *this_plot;
1018: {
1019: int_error("Binary file format for 2d data not yet defined", NO_CARET);
1020: return 0; /* keep compiler happy */
1021: }
1022: /*}}} */
1023:
1024: /*{{{ int df_3dmatrix(this_plot, ret_this_iso) */
1025: /*
1026: * formerly in gnubin.c
1027: *
1028: * modified by div for 3.6
1029: * obey the 'every' field from df_open
1030: * outrange points are marked as such, not omitted
1031: * obey using - treat x as column 1, y as col 2 and z as col 3
1032: * ( ie $1 gets x, $2 gets y, $3 gets z)
1033: *
1034: * we are less optimal for case of log plot and no using spec,
1035: * (call log too often) but that is price for flexibility
1036: * I suspect it didn't do autoscaling of x and y for log scale
1037: * properly ?
1038: *
1039: * Trouble figuring out file format ! Is it
1040:
1041: width x1 x2 x3 x4 x5 ...
1042: y1 z11 z12 z13 z14 z15 ...
1043: y2 x21 z22 z23 .....
1044: . .
1045: . .
1046: . .
1047:
1048: * with perhaps x and y swapped...
1049: *
1050: * - presumably rows continue to end of file, hence no indexing...
1051: *
1052: * Last update: 3/3/92 for Gnuplot 3.24.
1053: * Created from code for written by RKC for gnuplot 2.0b.
1054: *
1055: * 19 September 1992 Lawrence Crowl (crowl@cs.orst.edu)
1056: * Added user-specified bases for log scaling.
1057: *
1058: * Copyright (c) 1991,1992 Robert K. Cunningham, MIT Lincoln Laboratory
1059: *
1060: */
1061:
1062: /*
1063: Here we keep putting new plots onto the end of the linked list
1064:
1065: We assume the data's x,y values have x1<x2, x2<x3... and
1066: y1<y2, y2<y3... .
1067: Actually, I think the assumption is less strong than that--it looks like
1068: the direction just has to be the same.
1069: This routine expects the following to be properly initialized:
1070: is_log_x, is_log_y, and is_log_z
1071: base_log_x, base_log_y, and base_log_z
1072: log_base_log_x, log_base_log_y, and log_base_log_z
1073: xmin,ymin, and zmin
1074: xmax,ymax, and zmax
1075: autoscale_lx, autoscale_ly, and autoscale_lz
1076:
1077: does the autoscaling into the array versions (min_array[], max_array[])
1078: */
1079:
1080: int df_3dmatrix(this_plot)
1081: struct surface_points *this_plot;
1082: {
1083: float GPFAR *GPFAR * dmatrix, GPFAR * rt, GPFAR * ct;
1084: int nr, nc;
1085: int width, height;
1086: int row, col;
1087: struct iso_curve *this_iso;
1088: double used[3]; /* output from using manip */
1089: struct coordinate GPHUGE *point; /* HBB 980308: added 'GPHUGE' flag */
1090:
1091: assert(df_matrix);
1092:
1093: if (df_eof)
1094: return 0; /* hope caller understands this */
1095:
1096: if (df_binary) {
1097: if (!fread_matrix(data_fp, &dmatrix, &nr, &nc, &rt, &ct))
1098: int_error("Binary file read error: format unknown!", NO_CARET);
1099: /* fread_matrix() drains the file */
1100: df_eof = 1;
1101: } else {
1102: if (!(dmatrix = df_read_matrix(&nr, &nc))) {
1103: df_eof = 1;
1104: return 0;
1105: }
1106: rt = NULL;
1107: ct = NULL;
1108: }
1109:
1110: if (nc == 0 || nr == 0)
1111: int_error("Read grid of zero height or zero width", NO_CARET);
1112:
1113: this_plot->plot_type = DATA3D;
1114: this_plot->has_grid_topology = TRUE;
1115:
1116: if (df_no_use_specs != 0 && df_no_use_specs != 3)
1117: int_error("Current implementation requires full using spec", NO_CARET);
1118:
1119: if (df_max_cols < 3 &&
1120: !(df_column = (df_column_struct *) gp_realloc(df_column, (df_max_cols = 3) * sizeof(df_column_struct), "datafile columns"))
1121: )
1122: int_error("Out of store in binary read", c_token);
1123:
1124: df_no_cols = 3;
1125: df_column[0].good = df_column[1].good = df_column[2].good = DF_GOOD;
1126:
1127: assert(everyline > 0);
1128: assert(everypoint > 0);
1129: width = (nc - firstpoint + everypoint - 1) / everypoint; /* ? ? ? ? ? */
1130: height = (nr - firstline + everyline - 1) / everyline; /* ? ? ? ? ? */
1131:
1132: for (row = firstline; row < nr; row += everyline) {
1133: df_column[1].datum = rt ? rt[row] : row;
1134:
1135: /*Allocate the correct number of entries */
1136: this_iso = iso_alloc(width);
1137:
1138: point = this_iso->points;
1139:
1140: /* Cycle through data */
1141: for (col = firstpoint; col < nc; col += everypoint, ++point) {
1142: /*{{{ process one point */
1143: int i;
1144:
1145: df_column[0].datum = ct ? ct[col] : col;
1146: df_column[2].datum = dmatrix[row][col];
1147:
1148: /*{{{ pass through using spec */
1149: for (i = 0; i < 3; ++i) {
1150: int column = use_spec[i].column;
1151:
1152: if (df_no_use_specs == 0)
1153: used[i] = df_column[i].datum;
1154: else if (use_spec[i].at) {
1155: struct value a;
1156: evaluate_at(use_spec[i].at, &a);
1157: if (undefined) {
1158: point->type = UNDEFINED;
1159: goto skip; /* continue _outer_ loop */
1160: }
1161: used[i] = real(&a);
1162: } else if (column < 1 || column > df_no_cols) {
1163: point->type = UNDEFINED;
1164: goto skip;
1165: } else
1166: used[i] = df_column[column - 1].datum;
1167: }
1168: /*}}} */
1169:
1170: point->type = INRANGE; /* so far */
1171:
1172: /*{{{ autoscaling/clipping */
1173: /*{{{ autoscale/range-check x */
1174: if (used[0] > 0 || !is_log_x) {
1175: if (used[0] < min_array[FIRST_X_AXIS]) {
1176: if (autoscale_lx & 1)
1177: min_array[FIRST_X_AXIS] = used[0];
1178: else
1179: point->type = OUTRANGE;
1180: }
1181: if (used[0] > max_array[FIRST_X_AXIS]) {
1182: if (autoscale_lx & 2)
1183: max_array[FIRST_X_AXIS] = used[0];
1184: else
1185: point->type = OUTRANGE;
1186: }
1187: }
1188: /*}}} */
1189:
1190: /*{{{ autoscale/range-check y */
1191: if (used[1] > 0 || !is_log_y) {
1192: if (used[1] < min_array[FIRST_Y_AXIS]) {
1193: if (autoscale_ly & 1)
1194: min_array[FIRST_Y_AXIS] = used[1];
1195: else
1196: point->type = OUTRANGE;
1197: }
1198: if (used[1] > max_array[FIRST_Y_AXIS]) {
1199: if (autoscale_ly & 2)
1200: max_array[FIRST_Y_AXIS] = used[1];
1201: else
1202: point->type = OUTRANGE;
1203: }
1204: }
1205: /*}}} */
1206:
1207: /*{{{ autoscale/range-check z */
1208: if (used[2] > 0 || !is_log_z) {
1209: if (used[2] < min_array[FIRST_Z_AXIS]) {
1210: if (autoscale_lz & 1)
1211: min_array[FIRST_Z_AXIS] = used[2];
1212: else
1213: point->type = OUTRANGE;
1214: }
1215: if (used[2] > max_array[FIRST_Z_AXIS]) {
1216: if (autoscale_lz & 2)
1217: max_array[FIRST_Z_AXIS] = used[2];
1218: else
1219: point->type = OUTRANGE;
1220: }
1221: }
1222: /*}}} */
1223: /*}}} */
1224:
1225: /*{{{ log x */
1226: if (is_log_x) {
1227: if (used[0] < 0.0) {
1228: point->type = UNDEFINED;
1229: goto skip;
1230: } else if (used[0] == 0.0) {
1231: point->type = OUTRANGE;
1232: used[0] = -VERYLARGE;
1233: } else
1234: used[0] = log(used[0]) / log_base_log_x;
1235: }
1236: /*}}} */
1237:
1238: /*{{{ log y */
1239: if (is_log_y) {
1240: if (used[1] < 0.0) {
1241: point->type = UNDEFINED;
1242: goto skip;
1243: } else if (used[1] == 0.0) {
1244: point->type = OUTRANGE;
1245: used[1] = -VERYLARGE;
1246: } else
1247: used[1] = log(used[1]) / log_base_log_y;
1248: }
1249: /*}}} */
1250:
1251: /*{{{ log z */
1252: if (is_log_z) {
1253: if (used[2] < 0.0) {
1254: point->type = UNDEFINED;
1255: goto skip;
1256: } else if (used[2] == 0.0) {
1257: point->type = OUTRANGE;
1258: used[2] = -VERYLARGE;
1259: } else
1260: used[2] = log(used[2]) / log_base_log_z;
1261: }
1262: /*}}} */
1263:
1264: point->x = used[0];
1265: point->y = used[1];
1266: point->z = used[2];
1267:
1268:
1269: /* some of you wont like this, but I say goto is for this */
1270:
1271: skip:
1272: ; /* ansi requires this */
1273: /*}}} */
1274: }
1275: this_iso->p_count = width;
1276: this_iso->next = this_plot->iso_crvs;
1277: this_plot->iso_crvs = this_iso;
1278: this_plot->num_iso_read++;
1279: }
1280:
1281: free_matrix(dmatrix, 0, nr - 1, 0, nc - 1);
1282: if (rt)
1283: free_vector(rt, 0, nr - 1);
1284: if (ct)
1285: free_vector(ct, 0, nc - 1);
1286: return (nc);
1287: }
1288: /*}}} */
1289:
1290: /* stuff for implementing the call-backs for picking up data values
1291: * do it here so we can make the variables private to this file
1292: */
1293:
1294: /*{{{ void f_dollars(x) */
1295: void f_dollars(x)
1296: union argument *x;
1297: {
1298: int column = x->v_arg.v.int_val - 1;
1299: /* we checked it was an integer >= 0 at compile time */
1300: struct value a;
1301:
1302: if (column == -1) {
1303: push(Gcomplex(&a, (double) df_datum, 0.0)); /* $0 */
1304: } else if (column >= df_no_cols || df_column[column].good != DF_GOOD) {
1305: undefined = TRUE;
1306: push(&(x->v_arg)); /* this okay ? */
1307: } else
1308: push(Gcomplex(&a, df_column[column].datum, 0.0));
1309: }
1310: /*}}} */
1311:
1312: /*{{{ void f_column() */
1313: void f_column()
1314: {
1315: struct value a;
1316: int column;
1317: (void) pop(&a);
1318: column = (int) real(&a) - 1;
1319: if (column == -2)
1320: push(Ginteger(&a, line_count));
1321: else if (column == -1) /* $0 = df_datum */
1322: push(Gcomplex(&a, (double) df_datum, 0.0));
1323: else if (column < 0 || column >= df_no_cols || df_column[column].good != DF_GOOD) {
1324: undefined = TRUE;
1325: push(&a); /* any objection to this ? */
1326: } else
1327: push(Gcomplex(&a, df_column[column].datum, 0.0));
1328: }
1329: /*}}} */
1330:
1331: /*{{{ void f_valid() */
1332: void f_valid()
1333: {
1334: struct value a;
1335: int column, good;
1336: (void) pop(&a);
1337: column = (int) magnitude(&a) - 1;
1338: good = column >= 0 && column < df_no_cols && df_column[column].good == DF_GOOD;
1339: push(Ginteger(&a, good));
1340: }
1341:
1342: /*}}} */
1343:
1344: /*{{{ void f_timecolumn() */
1345: void f_timecolumn()
1346: {
1347: struct value a;
1348: int column;
1349: struct tm tm;
1350: (void) pop(&a);
1351: column = (int) magnitude(&a) - 1;
1352: if (column < 0 || column >= df_no_cols ||
1353: !df_column[column].position ||
1354: !gstrptime(df_column[column].position, timefmt, &tm)
1355: ) {
1356: undefined = TRUE;
1357: push(&a); /* any objection to this ? */
1358: } else
1359: push(Gcomplex(&a, gtimegm(&tm), 0.0));
1360: }
1361: /*}}} */
1362:
1363: #if 0 /* not used */
1364: /* count columns in timefmt */
1365: /*{{{ static int get_time_cols(fmt) */
1366: static int get_time_cols(fmt)
1367: char *fmt; /* format string */
1368: {
1369: int cnt, i;
1370: char *p;
1371:
1372: p = fmt;
1373: cnt = 0;
1374: while (isspace(*p))
1375: p++;
1376: if (!strlen(p))
1377: int_error("Empty time-data format", NO_CARET);
1378: cnt++;
1379: for (i = 0; i < strlen(p) - 1; i++) {
1380: if (isspace(p[i]) && !isspace(p[i + 1]))
1381: cnt++;
1382: }
1383: return (cnt);
1384: }
1385: /*}}} */
1386:
1387: /* modify default use_spec, applies for no user spec and time datacolumns */
1388: /*{{{ static void mod_def_usespec(specno,jump) */
1389: static void mod_def_usespec(specno, jump)
1390: int specno; /* which spec in ?:?:? */
1391: int jump; /* no of columns in timefmt (time data) */
1392: {
1393: int i;
1394:
1395: for (i = specno + 1; i < NCOL; ++i)
1396: use_spec[i].column += jump; /* add no of columns in time to the rest */
1397: df_no_use_specs = 0;
1398: }
1399: /*}}} */
1400: #endif /* not used */
1401:
1402: /*{{{ static int check_missing(s) */
1403: static int check_missing(s)
1404: char *s;
1405: {
1406: if (missing_val != NULL) {
1407: int len = strlen(missing_val);
1408: if (strncmp(s, missing_val, len) == 0 &&
1409: (isspace((int)s[len]) || !s[len])) {
1410: return (1);; /* store undefined point in plot */
1411: }
1412: }
1413: return (0);
1414: }
1415: /*}}} */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>