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