home *** CD-ROM | disk | FTP | other *** search
- #ifndef lint
- static char *RCSid = "$Id: datafile.c,v 1.17 1995/12/12 22:10:46 drd Exp $";
- #endif
-
-
- /*
- * this file provides the functions to handle data-file reading..
- * takes care of all the pipe / stdin / index / using worries
- */
-
- /*{{{ notes*/
- /* couldn't decide how to implement 'thru' only for 2d and 'index'
- * for only 3d, so I did them for both - I can see a use for
- * index in 2d, especially for fit.
- *
- * I keep thru for backwards compatibility, and extend it to allow
- * more natural plot 'data' thru f(y) - I (personally) prefer
- * my syntax, but then I'm biased...
- *
- * - because I needed it, I have added a range of indexes...
- * (s)plot 'data' [index i[:j]]
- *
- * also every a:b:c:d:e:f - plot every a'th point from c to e,
- * in every b lines from d to f
- * ie for (line=d; line<=f; line+=b)
- * for (point=c; point >=e; point+=a)
- *
- *
- * I dont like mixing this with the time series hack... I am
- * very into modular code, so I would prefer to not have to
- * have _anything_ to do with time series... for example,
- * we just look at columns in file, and that is independent
- * of 2d/3d. I really dont want to have to pass a flag to
- * this is plot or splot.
- *
- * use a global array df_timecol[] - cleared by df_open, then
- * columns needing time set by client.
- *
- * Now that df_2dbinary() and df_3dbinary() are here, I am seriously
- * tempted to move get_data() and get_3ddata() in here too
- *
- * public variables declared in this file.
- * int df_no_use_specs - number of columns specified with 'using'
- * int df_line_number - for error reporting
- * int df_datum - increases with each data point
- * TBOOLEAN df_binary - it's a binary file
- * [ might change this to return value from df_open() ]
- * int df_eof - end of file
- * int df_timecol[] - client controls which cols read as time
- *
- * functions
- * int df_open(int max_using)
- * parses thru / index / using on command line
- * max_using is max no of 'using' columns allowed
- * returns number of 'using' cols specified, or -1 on error (?)
- *
- * int df_readline(double vector[], int max)
- * reads a line, does all the 'index' and 'using' manipulation
- * deposits values into vector[]
- * returns
- * number of columns parsed [0=not blank line, but no valid data],
- * DF_EOF for EOF
- * DF_UNDEFINED - undefined result during eval of extended using spec
- * DF_FIRST_BLANK for first consecutive blank line
- * DF_SECOND_BLANK for second consecutive blank line
- * will return FIRST before SECOND
- *
- * if a using spec was given, lines not fulfilling spec are ignored.
- * we will always return exactly the number of items specified
- *
- * if no spec given, we return number of consecutive columns we parsed.
- *
- * if we are processing indexes, seperated by 'n' blank lines,
- * we will return n-1 blank lines before noticing the index change
- *
- * void df_close()
- * closes a currently open file.
- *
- * void f_dollars(x)
- * void f_column() actions for expressions using $i, column(j), etc
- * void f_valid()
- *
- *
- * line parsing slightly differently from previous versions of gnuplot...
- * given a line containing fewer columns than asked for, gnuplot used to make
- * up values... I say that if I have explicitly said 'using 1:2:3', then if
- * column 3 doesn't exist, I dont want this point...
- * a column number of 0 means generate a value... as before, this value
- * is useful in 2d, but probably meaningless in 3d ???
- *
- * 20/5/95 : accept 1.23d4 in place of e (but not in scanf string)
- * : autoextend data line buffer and MAX_COLS
- *
- */
- /*}}}*/
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <math.h>
- #include <ctype.h>
- #include <time.h>
- #include <assert.h>
-
- #include "plot.h"
- #include "fnproto.h" /* check prototypes against our defns */
- #include "binary.h"
- #include "setshow.h"
-
- /* if you change this, change the scanf in readline */
- #define NCOL 7 /* max using specs */
-
- /*{{{ static fns*/
- static int get_time_cols __P((char *fmt));
- static void mod_def_usespec __P((int specno, int jump));
- static int check_missing __P((char *s));
- /*}}}*/
-
- /*{{{ variables*/
- struct use_spec_s { int column; struct at_type *at; };
-
- /* public variables client might access */
-
- int df_no_use_specs; /* how many using columns were specified */
- int df_line_number;
- int df_datum; /* suggested x value if none given */
- TBOOLEAN df_binary = FALSE; /* this is a binary file */
- int df_eof=0;
- int df_timecol[NCOL];
-
- /* private variables */
-
- /* in order to allow arbitrary data line length, we need to use the heap
- * might consider free-ing it in df_close, especially for small systems
- */
- static char *line=NULL;
- static int max_line_len=0;
-
- static FILE *data_fp=NULL;
- static TBOOLEAN pipe_open = FALSE;
- static TBOOLEAN mixed_data_fp = FALSE;
-
- #ifndef MAXINT /* should there be one already defined ? */
- #ifdef INT_MAX /* in limits.h ? */
- #define MAXINT INT_MAX
- #else
- #define MAXINT ((~0)>>1)
- #endif
- #endif
-
- /* stuff for implementing index */
- static int blank_count=0; /* how many blank lines recently */
- static int df_lower_index=0; /* first mesh required */
- static int df_upper_index=MAXINT;
- static int df_index_step=1; /* 'every' for indices */
- static int df_current_index; /* current mesh */
-
- /* stuff for every point:line */
- static int everypoint=1;
- static int firstpoint=0;
- static int lastpoint=MAXINT;
- static int everyline=1;
- static int firstline=0;
- static int lastline=MAXINT;
- static int point_count=-1; /* point counter - preincrement and test 0 */
- static int line_count=0; /* line counter */
-
- /* parsing stuff */
- static struct use_spec_s use_spec[NCOL];
- static char df_format[MAX_LINE_LEN+1];
-
- /* rather than three arrays which all grow dynamically, make one
- * dynamic array of this structure
- */
-
- typedef struct df_column_struct {
- double datum;
- int good; /* 0=bad number (might still be a time), 1=good, -1=missing */
- char *position;
- } df_column_struct;
-
- static df_column_struct *df_column=NULL; /* we'll allocate space as needed */
- static int df_max_cols=0; /* space allocated */
- static int df_no_cols; /* cols read */
-
- /* external variables we need */
-
- extern int c_token, num_tokens;
- extern char timefmt[]; /* I would rather not need this, but ... */
- /* columns needing timefmt are passed in df_timecol[] after df_open */
-
- /* jev -- for passing data thru user-defined function */
- extern struct udft_entry ydata_func;
- extern struct udft_entry *dummy_func;
- extern char dummy_var[MAX_NUM_VAR][MAX_ID_LEN+1];
- extern char c_dummy_var[MAX_NUM_VAR][MAX_ID_LEN+1];
-
- extern double min_array[], max_array[];
- /*}}}*/
-
- /*{{{ replacement sscanf for purec*/
- #ifdef __PUREC__
- /*
- * a substitute for PureC's buggy sscanf.
- * this uses the normal sscanf and fixes the following bugs:
- * - whitespace in format matches whitespace in string, but doesn't
- * require any. ( "%f , %f" scans "1,2" correctly )
- * - the ignore value feature works (*). this created an address error
- * in PureC.
- */
-
- #include <stdarg.h>
- #include <string.h>
-
- int purec_sscanf( const char *string, const char *format, ... )
- {
- va_list args;
- int cnt=0;
- char onefmt[256];
- char buffer[256];
- const char *f=format;
- const char *s=string;
- char *f2;
- char ch;
- int ignore;
- void *p;
- int *ip;
- int pos;
-
- va_start(args,format);
- while( *f && *s ) {
- ch=*f++;
- if( ch!='%' ) {
- if(isspace(ch)) {
- /* match any number of whitespace */
- while(isspace(*s)) s++;
- } else {
- /* match exactly the character ch */
- if( *s!=ch ) goto finish;
- s++;
- }
- } else {
- /* we have got a '%' */
- ch=*f++;
- if( ch=='%' ) {
- /* match exactly % */
- if( *s!=ch ) goto finish;
- s++;
- } else {
- f2=onefmt;
- *f2++='%';
- *f2++=ch;
- ignore=0;
- if( ch=='*' ) {
- ignore=1;
- ch=f2[-1]=*f++;
- }
- while( isdigit(ch) ) {
- ch=*f2++=*f++;
- }
- if( ch=='l' || ch=='L' || ch=='h' ) {
- ch=*f2++=*f++;
- }
- switch(ch) {
- case '[':
- while( ch && ch!=']' ) {
- ch=*f2++=*f++;
- }
- if( !ch ) goto error;
- break;
- case 'e':
- case 'f':
- case 'g':
- case 'd':
- case 'o':
- case 'i':
- case 'u':
- case 'x':
- case 'c':
- case 's':
- case 'p':
- case 'n': /* special case handled below */
- break;
- default:
- goto error;
- }
- if( ch!='n' ) {
- strcpy(f2,"%n");
- if( ignore ) {
- p=buffer;
- } else {
- p=va_arg(args,void *);
- }
- switch( sscanf( s, onefmt, p, &pos ) ) {
- case EOF: goto error;
- case 0 : goto finish;
- }
- if( !ignore ) cnt++;
- s+=pos;
- } else {
- if( !ignore ) {
- ip=va_arg(args,int *);
- *ip=(int)(s-string);
- }
- }
- }
- }
- }
-
- if( !*f ) goto finish;
-
- error:
- cnt=EOF;
- finish:
- va_end(args);
- return cnt;
- }
-
- /* use the substitute now. I know this is dirty trick, but it works. */
- #define sscanf purec_sscanf
-
- #endif /* __PUREC__ */
- /*}}}*/
-
- /*{{{ int df_open(max_using)*/
- int df_open(max_using)
- int max_using;
-
- /* open file, parsing using/thru/index stuff
- * return number of using specs [well, we have to return something !]
- */
-
- {
- static char filename[MAX_LINE_LEN+1]="";
- int i;
- int name_token;
-
- /*{{{ close file if necessary*/
- if (data_fp)
- df_close();
- /*}}}*/
-
- /*{{{ initialise static variables*/
- df_format[0] = '\0'; /* no format string */
-
- df_no_use_specs = 0;
-
- for (i=0; i<NCOL; ++i)
- { use_spec[i].column=i+1; /* default column */
- use_spec[i].at = NULL; /* no expression */
- }
-
- if (max_using > NCOL)
- max_using = NCOL;
-
- df_datum = 0;
- df_line_number=0;
-
- df_lower_index = 0;
- df_index_step = 1;
- df_upper_index = MAXINT;
-
- df_current_index=0;
- blank_count = 2;
- /* by initialising blank_count, leading blanks will be ignored */
-
- everypoint = everyline=1; /* unless there is an every spec */
- firstpoint=firstline=0;
- lastpoint=lastline=MAXINT;
-
- df_eof=0;
-
- memset(df_timecol, 0, sizeof(df_timecol));
-
- df_binary=1;
- /*}}}*/
-
- assert(max_using <= NCOL);
-
- /* empty name means re-use last one */
-
- { char name[MAX_LINE_LEN+1];
- quote_str(name, c_token, MAX_LINE_LEN);
- if (name[0])
- strcpy(filename, name);
- else if (!filename[0])
- int_error("No previous filename", c_token);
- }
- name_token = c_token++;
-
- /* defer opening until we have parsed the modifiers... */
-
- /*{{{ look for binary*/
- if (almost_equals(c_token, "bin$ary"))
- {
- ++c_token;
- df_binary=TRUE;
- }
- else
- df_binary=FALSE;
- /*}}}*/
-
- /*{{{ deal with index*/
- if (almost_equals(c_token, "i$ndex")) {
- struct value a;
-
- if (df_binary)
- int_error("Binary file format does not allow more than one surface per file",c_token);
-
- ++c_token;
- df_lower_index = (int)real(const_express(&a));
- if (equals(c_token, ":")) {
- ++c_token;
- df_upper_index = (int)magnitude(const_express(&a));
- if (df_upper_index < df_lower_index)
- int_error("Upper index should be bigger than lower index", c_token);
-
- if (equals(c_token, ":")) {
- ++c_token;
- df_index_step = (int)magnitude(const_express(&a));
- if (df_index_step < 1)
- int_error("Index step must be positive", c_token);
- }
- }
- else
- df_upper_index = df_lower_index;
- }
- /*}}}*/
-
- /*{{{ deal with every*/
- if (almost_equals(c_token, "ev$ery")) {
- struct value a;
-
- /* allow empty fields - every a:b:c::e
- * we have already established the defaults
- */
-
- if (!equals(++c_token, ":")) {
- everypoint = (int)real(const_express(&a));
- if (everypoint<1)
- int_error("Expected positive integer", c_token);
- }
-
- /* if it fails on first test, no more tests will succeed. If it
- * fails on second test, next test will succeed with correct c_token
- */
- if (equals(c_token, ":") && !equals(++c_token, ":")) {
- everyline = (int)real(const_express(&a));
- if (everyline<1)
- int_error("Expected positive integer", c_token);
- }
-
- if (equals(c_token, ":") && !equals(++c_token, ":")) {
- firstpoint = (int)real(const_express(&a));
- if (firstpoint<0)
- int_error("Expected non-negative integer", c_token);
- }
-
- if (equals(c_token, ":") && !equals(++c_token, ":")) {
- firstline = (int)real(const_express(&a));
- if (firstline<0)
- int_error("Expected non-negative integer", c_token);
- }
-
- if (equals(c_token, ":") && !equals(++c_token, ":")) {
- lastpoint = (int)real(const_express(&a));
- if (lastpoint<firstpoint)
- int_error("Last point must not be before first point", c_token);
- }
-
- if (equals(c_token, ":")) {
- ++c_token;
- lastline = (int)real(const_express(&a));
- if (lastline<firstline)
- int_error("Last line must not be before first line", c_token);
- }
- }
- /*}}}*/
-
- /*{{{ deal with thru*/
- /* jev -- support for passing data from file thru user function */
-
- if (almost_equals(c_token, "thru$")) {
- c_token++;
- if (ydata_func.at)
- free(ydata_func.at);
- strcpy(c_dummy_var[0], dummy_var[0]);
- /* allow y also as a dummy variable.
- * during plot, c_dummy_var[0] and [1] are 'sacred'
- * ie may be set by splot [u=1:2] [v=1:2], and these
- * names are stored only in c_dummy_var[]
- * so choose dummy var 2 - can anything vital be here ?
- */
- dummy_func = &ydata_func;
- strcpy(c_dummy_var[2], "y");
- ydata_func.at = perm_at();
- dummy_func = NULL;
- } else {
- if (ydata_func.at)
- free(ydata_func.at);
- ydata_func.at = NULL;
- }
- /*}}}*/
-
- /*{{{ deal with using*/
- if (almost_equals(c_token,"u$sing"))
- {
- if (!END_OF_COMMAND && !isstring(++c_token))
- {
- struct value a;
-
- do /* must be at least one */
- {
- if (df_no_use_specs >= max_using)
- int_error("Too many columns in using specification", c_token);
-
- if (equals(c_token, "("))
- {
- dummy_func=NULL; /* no dummy variables active */
- use_spec[df_no_use_specs++].at = perm_at(); /* it will match ()'s */
- }
- else
- use_spec[df_no_use_specs++].column =
- (int)magnitude(const_express(&a));
- } while (equals(c_token,":") && ++c_token);
- }
-
- if (!END_OF_COMMAND && isstring(c_token)) {
- if (df_binary)
- int_error("Format string meaningless with binary data",NO_CARET);
-
- quote_str(df_format, c_token, MAX_LINE_LEN);
- if (!valid_format( df_format))
- int_error( "Please use a double conversion %lf", c_token);
-
- c_token++; /* skip format */
- }
- }
- /*}}}*/
-
- /*{{{ more variable inits*/
- point_count = -1; /* we preincrement */
- line_count = 0;
-
- /* here so it's not done for every line in df_readline */
- if (max_line_len < 160)
- line = (char *)alloc(max_line_len=160, "datafile line buffer");
-
-
- /*}}}*/
-
-
- /*{{{ open file*/
- #if defined(unix) || defined(PIPES)
- if (*filename == '<') {
- if ((data_fp = popen(filename + 1, "r")) == (FILE *) NULL)
- os_error("cannot create pipe for data", name_token);
- else
- pipe_open = TRUE;
- } else
- #endif /* unix || PIPES */
- if (*filename == '-'){
- data_fp=lf_top();
- if(!data_fp) data_fp=stdin;
- mixed_data_fp=TRUE; /* don't close command file */
- } else
- if ((data_fp = fopen(filename, "r")) == (FILE *) NULL)
- os_error("can't open data file", name_token);
- /*}}}*/
-
- return df_no_use_specs;
- }
- /*}}}*/
-
- /*{{{ void df_close()*/
- void df_close()
- {
- int i;
-
- df_no_cols = 0; /* paranoid - mark $n and column(n) as invalid */
-
- if (!data_fp)
- return;
-
- if (ydata_func.at) {
- free(ydata_func.at);
- ydata_func.at = NULL;
- }
-
- /*{{{ free any use expression storage*/
- for (i=0; i<df_no_use_specs; ++i)
- if (use_spec[i].at)
- {
- free(use_spec[i].at);
- use_spec[i].at = NULL;
- }
- /*}}}*/
-
- if(!mixed_data_fp) {
- #if defined(unix) || defined(PIPES)
- if (pipe_open) {
- (void) pclose(data_fp);
- pipe_open = FALSE;
- } else
- #endif /* unix || PIPES */
- (void) fclose(data_fp);
- }
- mixed_data_fp=FALSE;
- data_fp=NULL;
- }
- /*}}}*/
-
- /*{{{ int df_readline(v, max)*/
- /* do the hard work... read lines from file,
- * - use blanks to get index number
- * - ignore lines outside range of indices required
- * - fill v[] based on using spec if given
- */
-
- int df_readline(v, max)
- double v[];
- int max;
- {
- assert(data_fp != NULL);
- assert(!df_binary);
- assert(max <= NCOL);
- assert(max_line_len); /* alloc-ed in df_open() */
-
- /* catch attempt to read past EOF on mixed-input */
- if (df_eof)
- return DF_EOF;
-
- while (fgets(line, max_line_len-1, data_fp) != (char *) NULL)
- /*{{{ process line*/
- {
- char *s;
- int line_okay = 1;
- int output=0; /* how many numbers written to v[] */
-
- /*{{{ if line was longer than our buffer, realloc and read some more*/
- {
- int len=strlen(line);
- while (line[len-1] != '\n') {
- /* buffer we provided may not be full - dont grab extra
- * memory un-necessarily. This may trap a problem with last
- * line in file not being properly terminated - each time
- * through a replot loop, it was doubling buffer size
- */
- if ( (max_line_len-len) < 32)
- line = ralloc(line, max_line_len*=2, "datafile line buffer");
-
- if (!fgets(line+len-1, max_line_len - len, data_fp))
- break; /* unexpected end of file, but we have something to do */
-
- /* get length of just the new bit, rather than whole lot */
- len += strlen(line+len);
- }
- }
- /*}}}*/
-
- s=line;
- ++df_line_number;
- ++df_datum;
- df_no_cols = 0;
-
- /*{{{ check for blank lines, and reject by index/every*/
- /*{{{ skip leading spaces*/
- while (isspace(*s))
- ++s; /* will skip the \n too, to point at \0 */
- /*}}}*/
-
- /*{{{ skip comments*/
- if (is_comment(*s))
- continue; /* ignore comments */
- /*}}}*/
-
- /*{{{ check EOF on mixed data*/
- if (mixed_data_fp && is_EOF(*s))
- {
- df_eof=1; /* trap attempts to read past EOF */
- return DF_EOF;
- }
- /*}}}*/
-
- if (*s==0)
- /*{{{ its a blank line - update counters and continue or return*/
- {
- /* argh - this is complicated ! we need to
- * ignore it if we haven't reached first index
- * report EOF if passed last index
- * report blank line unless we've already done 2 blank lines
- *
- * - I have probably missed some obvious way of doing all this,
- * but its getting late
- */
-
- point_count=-1; /* restart counter within line */
-
- if (++blank_count==1) {
- /* first blank line */
- ++line_count;
- }
-
- if (blank_count == 2)
- { /* just reached end of a group/surface */
- ++df_current_index;
- line_count = 0;
- df_datum = 0;
- /* ignore line if current_index has just become
- * first required one - client doesn't want this
- * blank line. While we're here, check for <=
- * - we need to do it outside this conditional, but
- * probably no extra cost at assembler level
- */
- if (df_current_index <= df_lower_index)
- continue; /* dont tell client */
-
- /* df_upper_index is MAXINT-1 if we are not doing index */
- if (df_current_index > df_upper_index)
- {
- /* oops - need to gobble rest of input if mixed */
- if (mixed_data_fp)
- continue;
- else
- {
- df_eof=1;
- return DF_EOF; /* no point continuing */
- }
- }
- }
-
- /* dont tell client if we haven't reached first index */
- if (df_current_index < df_lower_index)
- continue;
-
- /* ignore blank lines after blank_index */
- if (blank_count > 2)
- continue;
-
- return DF_FIRST_BLANK - (blank_count-1);
- }
- /*}}}*/
-
- /* get here => was not blank */
-
- blank_count = 0;
-
- /*{{{ ignore points outside range of index*/
- /* we try to return end-of-file as soon as we pass upper index,
- * but for mixed input stream, we must skip garbage
- */
-
- if (df_current_index < df_lower_index ||
- df_current_index > df_upper_index ||
- ((df_current_index - df_lower_index)%df_index_step) != 0)
- continue;
- /*}}}*/
-
- /*{{{ reject points by every*/
- /* accept only lines with (line_count%everyline)==0 */
-
- if (line_count<firstline || line_count > lastline ||
- (line_count-firstline)%everyline != 0
- )
- continue;
-
- /* update point_count. ignore point if point_count%everypoint != 0 */
-
- if (++point_count < firstpoint || point_count > lastpoint ||
- (point_count-firstpoint)%everypoint != 0
- )
- continue;
- /*}}}*/
- /*}}}*/
-
- if (*df_format)
- /*{{{ do a sscanf*/
- {
- int i;
-
- assert(NCOL==7);
-
- /* check we have room for at least 7 columns */
- if (df_max_cols < 7)
- df_column = (df_column_struct *)ralloc(df_column, (df_max_cols=7)*sizeof(df_column_struct), "datafile columns");
-
- df_no_cols = sscanf(line, df_format,
- &df_column[0].datum,
- &df_column[1].datum,
- &df_column[2].datum,
- &df_column[3].datum,
- &df_column[4].datum,
- &df_column[5].datum,
- &df_column[6].datum);
-
- if (df_no_cols == EOF)
- {
- df_eof=1;
- return DF_EOF; /* tell client */
- }
- for (i=0; i < df_no_cols; ++i ) /* may be zero */
- { df_column[i].good=1;
- df_column[i].position=NULL; /* cant get a time */
- }
- }
- /*}}}*/
- else
- /*{{{ read the data column by column*/
- {
- /* implement our own sscanf that skips lines with invalid
- * data if a using statement was given
- * convert the array to its constituents
- */
-
- df_no_cols = 0;
-
- while (*s)
- {
- int used;
-
- /* check store - double max cols or add 20, whichever is greater */
- if (df_max_cols <= df_no_cols)
- df_column=(df_column_struct *)ralloc(df_column, (df_max_cols += (df_max_cols < 20 ? 20 : df_max_cols))*sizeof(df_column_struct), "datafile column");
-
- /* have always skipped spaces at this point */
- df_column[df_no_cols].position = s;
-
- if (check_missing(s))
- df_column[df_no_cols].good = -1;
- else {
- /* cannot trust strtod - eg strtod("-",&p) */
- df_column[df_no_cols].good = sscanf(s, "%lf%n", &df_column[df_no_cols].datum, &used);
-
- /* it might be a fortran double or quad precision. 'used'
- * is only safe if good is 1
- */
-
- if (df_column[df_no_cols].good &&
- (s[used]=='d' || s[used]=='D' || s[used]=='q' || s[used]=='Q')
- ) {
- /* might be fortran double */
- s[used]='e';
- /* and try again */
- df_column[df_no_cols].good = sscanf(s, "%lf", &df_column[df_no_cols].datum);
- }
- }
-
- ++df_no_cols;
- /*{{{ skip chars to end of column*/
- while ((!isspace(*s)) && (*s != '\0'))
- ++s;
- /*}}}*/
- /*{{{ skip spaces to start of next column*/
- while (isspace(*s))
- ++s;
- /*}}}*/
- }
- }
- /*}}}*/
-
- /*{{{ copy column[] to v[] via use[]*/
- {
- int limit=(df_no_use_specs ? df_no_use_specs : NCOL);
- if (limit>max)
- limit=max;
-
- for (output=0; output<limit; ++output)
- {
- /* if there was no using spec, column is output+1 and at=NULL */
- int column=use_spec[output].column;
-
- if (use_spec[output].at)
- { struct value a;
- /* no dummy values to set up prior to... */
- evaluate_at(use_spec[output].at, &a);
- if (undefined)
- return DF_UNDEFINED; /* store undefined point in plot */
-
- v[output]=real(&a);
- }
- else if (df_timecol[output])
- { struct tm tm;
- if (column >= df_no_cols ||
- df_column[column-1].good == -1 /* missing */ ||
- !df_column[column-1].position ||
- !gstrptime(df_column[column-1].position,timefmt,&tm)
- )
- {
- /* line bad only if user explicitly asked for this column */
- if (df_no_use_specs)
- line_okay=0;
-
- /* return or ignore line depending on line_okay */
- break;
- }
- v[output] = (double) gtimegm(&tm);
- }
- else if (column)
- {
- v[output] = df_column[column-1].datum;
- if ( (column > df_no_cols) || df_column[column-1].good!=1)
- {
- /* line bad only if user explicitly asked for this column */
- if (df_no_use_specs)
- line_okay=0;
- break; /* return or ignore depending on line_okay */
- }
- }
- else
- v[output] = df_datum; /* using 0 */
- }
- }
- /*}}}*/
-
- if (!line_okay)
- continue;
-
- /* output == df_no_use_specs if using was specified
- * - actually, smaller of df_no_use_specs and max
- */
- assert (df_no_use_specs==0 || output==df_no_use_specs || output==max);
-
- return output;
-
- }
- /*}}}*/
-
- /* get here => fgets failed */
-
- df_no_cols = 0; /* no longer needed - mark column(x) as invalid */
- df_eof=1;
- return DF_EOF;
- }
- /*}}}*/
-
- /*{{{ int df_2dbinary(this_plot)*/
- int df_2dbinary(this_plot)
- struct curve_points *this_plot;
- {
- int_error("Binary file format for 2d data not yet defined", NO_CARET);
- return 0; /* keep compiler happy */
- }
- /*}}}*/
-
- /*{{{ int df_3dbinary(this_plot, ret_this_iso)*/
- /*
- * formerly in gnubin.c
- *
- * modified by div for 3.6
- * obey the 'every' field from df_open
- * outrange points are marked as such, not omitted
- * obey using - treat x as column 1, y as col 2 and z as col 3
- * ( ie $1 gets x, $2 gets y, $3 gets z)
- *
- * we are less optimal for case of log plot and no using spec,
- * (call log too often) but that is price for flexibility
- * I suspect it didn't do autoscaling of x and y for log scale
- * properly ?
- *
- * Trouble figuring out file format ! Is it
-
- width x1 x2 x3 x4 x5 ...
- y1 z11 z12 z13 z14 z15 ...
- y2 x21 z22 z23 .....
- . .
- . .
- . .
-
- * with perhaps x and y swapped...
- *
- * - presumably rows continue to end of file, hence no indexing...
- *
- * Last update: 3/3/92 for Gnuplot 3.24.
- * Created from code for written by RKC for gnuplot 2.0b.
- *
- * 19 September 1992 Lawrence Crowl (crowl@cs.orst.edu)
- * Added user-specified bases for log scaling.
- *
- * Copyright (c) 1991,1992 Robert K. Cunningham, MIT Lincoln Laboratory
- *
- */
-
- /*
- Here we keep putting new plots onto the end of the linked list
-
- We assume the data's x,y values have x1<x2, x2<x3... and
- y1<y2, y2<y3... .
- Actually, I think the assumption is less stron than that--it looks like
- the direction just has to be the same.
- This routine expects the following to be properly initialized:
- is_log_x, is_log_y, and is_log_z
- base_log_x, base_log_y, and base_log_z
- log_base_log_x, log_base_log_y, and log_base_log_z
- xmin,ymin, and zmin
- xmax,ymax, and zmax
- autoscale_lx, autoscale_ly, and autoscale_lz
-
- does the autoscaling into the array versions (min_array[], max_array[])
- */
-
- int
- df_3dbinary(this_plot)
- struct surface_points *this_plot;
- {
- float GPFAR * GPFAR *matrix, GPFAR *rt, GPFAR *ct;
- int nr,nc;
- int width,height;
- int row,col;
- struct iso_curve *this_iso;
- double used[3]; /* output from using manip */
- struct coordinate *point;
-
- assert(df_binary);
-
- if (df_eof)
- return 0; /* hope caller understands this */
-
- if(!fread_matrix(data_fp,&matrix,&nr,&nc,&rt,&ct))
- int_error("Binary file read error: format unknown!",NO_CARET);
-
- if (nc==0 || nr==0)
- int_error("Read grid of zero height or zero width", NO_CARET);
-
- /* fread_matrix() drains the file */
- df_eof=1;
-
- this_plot->plot_type = DATA3D;
- this_plot->has_grid_topology = TRUE;
-
- if (df_no_use_specs != 0 && df_no_use_specs != 3)
- int_error("Current implementation requires full using spec",NO_CARET);
-
- if (df_max_cols < 3 &&
- !(df_column = (df_column_struct *)ralloc(df_column, (df_max_cols=3)*sizeof(df_column_struct), "datafile columns"))
- )
- int_error("Out of store in binary read", c_token);
-
- df_no_cols=3;
- df_column[0].good=df_column[1].good=df_column[2].good=1;
-
- assert(everyline > 0);
- assert(everypoint > 0);
- width=(nc-firstpoint+everypoint-1)/everypoint; /* ? ? ? ? ? */
- height=(nr-firstline+everyline-1)/everyline; /* ? ? ? ? ? */
-
- for(row=firstline; row < nr; row+=everyline){
- df_column[1].datum=rt[row];
-
- this_iso = iso_alloc(width);/*Allocate the correct number of entries*/
- point=this_iso->points;
-
- for(col = firstpoint; col< nc; col+=everypoint, ++point){/* Cycle through data */
- /*{{{ process one row*/
- int i;
-
- df_column[0].datum=ct[col];
- df_column[2].datum=matrix[row][col];
-
- /*{{{ pass through using spec*/
- for (i=0; i<3; ++i)
- {
- int column=use_spec[i].column;
-
- if (df_no_use_specs==0)
- used[i]=df_column[i].datum;
- else if (use_spec[i].at)
- {
- struct value a;
- evaluate_at(use_spec[i].at, &a);
- if (undefined)
- {
- point->type=UNDEFINED;
- goto skip; /* continue _outer_ loop */
- }
- used[i]=real(&a);
- }
- else if (column < 1 || column > df_no_cols)
- {
- point->type=UNDEFINED;
- goto skip;
- }
- else
- used[i]=df_column[column-1].datum;
- }
- /*}}}*/
-
- point->type=INRANGE; /* so far */
-
- /*{{{ autoscaling/clipping*/
- /*{{{ autoscale/range-check x*/
- if (used[0]>0 || !is_log_x)
- {
- if (used[0] < min_array[FIRST_X_AXIS])
- { if (autoscale_lx & 1)
- min_array[FIRST_X_AXIS] = used[0];
- else
- point->type=OUTRANGE;
- }
-
- if (used[0] > max_array[FIRST_X_AXIS])
- { if (autoscale_lx & 2)
- max_array[FIRST_X_AXIS] = used[0];
- else
- point->type=OUTRANGE;
- }
- }
- /*}}}*/
-
- /*{{{ autoscale/range-check y*/
- if (used[1]>0 || !is_log_y)
- {
- if (used[0] < min_array[FIRST_Y_AXIS])
- { if (autoscale_ly & 1)
- min_array[FIRST_Y_AXIS] = used[1];
- else
- point->type=OUTRANGE;
- }
-
- if (used[1] > max_array[FIRST_Y_AXIS])
- { if (autoscale_ly & 2)
- max_array[FIRST_Y_AXIS] = used[0];
- else
- point->type=OUTRANGE;
- }
- }
- /*}}}*/
-
- /*{{{ autoscale/range-check z*/
- if (used[2]>0 || !is_log_z)
- {
- if (used[2] < min_array[FIRST_Z_AXIS])
- { if (autoscale_lz & 1)
- min_array[FIRST_Z_AXIS] = used[2];
- else
- point->type=OUTRANGE;
- }
-
- if (used[2] > max_array[FIRST_Z_AXIS])
- { if (autoscale_lz & 2)
- max_array[FIRST_Z_AXIS] = used[2];
- else
- point->type=OUTRANGE;
- }
- }
- /*}}}*/
- /*}}}*/
-
- /*{{{ log x*/
- if (is_log_x)
- {
- if (used[0] < 0.0)
- {
- point->type = UNDEFINED;
- goto skip;
- }
- else if (used[0] == 0.0)
- {
- point->type = OUTRANGE;
- used[0]=-VERYLARGE;
- }
- else
- used[0] = log(used[0])/log_base_log_x;
- }
- /*}}}*/
-
- /*{{{ log y*/
- if (is_log_y)
- {
- if (used[1] < 0.0)
- {
- point->type = UNDEFINED;
- goto skip;
- }
- else if (used[1] == 0.0)
- {
- point->type=OUTRANGE;
- used[1]=-VERYLARGE;
- }
- else
- used[1] = log(used[1])/log_base_log_y;
- }
- /*}}}*/
-
- /*{{{ log z*/
- if (is_log_z)
- {
- if (used[2] < 0.0)
- {
- point->type = UNDEFINED;
- goto skip;
- }
- else if (used[2]==0.0)
- {
- point->type = OUTRANGE;
- used[2]=-VERYLARGE;
- }
- else
- used[2] = log(used[2])/log_base_log_z;
- }
- /*}}}*/
-
- point->x = used[0];
- point->y = used[1];
- point->z = used[2];
-
-
- /* some of you wont like this, but I say goto is for this */
-
- skip:
- ; /* ansi requires this */
- /*}}}*/
- }
- this_iso->p_count = width;
- this_iso->next = this_plot->iso_crvs;
- this_plot->iso_crvs = this_iso;
- this_plot->num_iso_read++;
- }
-
- free_matrix(matrix,0,nr-1,0,nc-1);
- free_vector(rt,0,nr-1);
- free_vector(ct,0,nc-1);
- return(nc);
- }
- /*}}}*/
-
- /* stuff for implementing the call-backs for picking up data values
- * do it here so we can make the variables private to this file
- */
-
- /*{{{ void f_dollars(x)*/
- void f_dollars(x)
- union argument *x;
- {
- int column = x->v_arg.v.int_val - 1;
- /* we checked it was an integer >= 0 at compile time */
- struct value a;
-
- if (column==-1)
- {
- push ( Gcomplex(&a, (double)df_datum, 0.0)); /* $0 */
- }
- else if (column >= df_no_cols || !df_column[column].good)
- { undefined = TRUE;
- push (&(x->v_arg)); /* this okay ? */
- }
- else
- push( Gcomplex(&a, df_column[column].datum, 0.0) );
- }
- /*}}}*/
-
- /*{{{ void f_column()*/
- void f_column()
- {
- struct value a;
- int column;
- (void) pop(&a);
- column = (int) magnitude(&a) - 1;
- if (column < 0 || column >= df_no_cols || !df_column[column].good)
- { undefined = TRUE;
- push (&a); /* any objection to this ? */
- }
- else
- push( Gcomplex(&a, df_column[column].datum, 0.0) );
- }
- /*}}}*/
-
- /*{{{ void f_valid()*/
- void f_valid()
- {
- struct value a;
- int column,good;
- (void) pop(&a);
- column = (int) magnitude(&a) - 1;
- good = column >= 0 && column < df_no_cols && df_column[column].good;
- push (Ginteger(&a, good));
- }
- /*}}}*/
-
- /* count columns in timefmt */
- /*{{{ static int get_time_cols(fmt)*/
- static int get_time_cols(fmt)
- char *fmt; /* format string */
- {
- int cnt,i;
- char *p;
-
- p = fmt;
- cnt = 0;
- while ( isspace(*p) )
- p++;
- if ( ! strlen(p))
- int_error("Empty time-data format",NO_CARET);
- cnt ++;
- for(i=0;i<strlen(p)-1;i++) {
- if ( isspace(p[i]) && !isspace(p[i+1]) )
- cnt++;
- }
- return(cnt);
- }
- /*}}}*/
-
- /* modify default use_spec, applies for no user spec and time datacolumns */
- /*{{{ static void mod_def_usespec(specno,jump)*/
- static void mod_def_usespec(specno,jump)
- int specno; /* which spec in ?:?:? */
- int jump; /* no of columns in timefmt (time data) */
- {
- int i;
-
- for (i=specno+1; i<NCOL; ++i)
- use_spec[i].column += jump; /* add no of columns in time to the rest */
- df_no_use_specs = 0;
- }
- /*}}}*/
-
- /*{{{ static int check_missing(s)*/
- static int check_missing(s)
- char *s;
- {
- if ( missing_val != NULL ) {
- if ( !strncmp(s,missing_val,strlen(missing_val)) && isspace(s[strlen(missing_val)])) {
- return (1);; /* store undefined point in plot */
- }
- }
- return(0);
- }
- /*}}}*/
-