#ifndef lint
static char *RCSid() { return RCSid("$Id: datafile.c,v 1.110.2.20 2009/03/13 06:23:07 sfeam Exp $"); }
#endif
/* GNUPLOT - datafile.c */
/*[
* Copyright 1986 - 1993, 1998, 2004 Thomas Williams, Colin Kelley
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose with or without fee is hereby granted,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation.
*
* Permission to modify the software is granted, but not the right to
* distribute the complete modified source code. Modifications are to
* be distributed as patches to the released version. Permission to
* distribute binaries produced by compiling modified sources is granted,
* provided you
* 1. distribute the corresponding source modifications from the
* released version in the form of a patch file along with the binaries,
* 2. add special version identification to distinguish your version
* in addition to the base release version number,
* 3. provide your name and address as the primary contact for the
* support of your modified version, and
* 4. retain our contact information in regard to use of the base
* software.
* Permission to distribute the released version of the source code along
* with corresponding source modifications in the form of a patch file is
* granted with same provisions 2 through 4 for binary distributions.
*
* This software is provided "as is" without express or implied warranty
* to the extent permitted by applicable law.
]*/
/* AUTHOR : David Denholm */
/*
* 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=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.
*
* 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_no_tic_specs - count of additional ticlabel columns
* 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
*
* functions
* int df_open(char *file_name, 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_MISSING - requested column matched that of 'set missing '
* 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, separated 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 as an x value, and is reset at blank lines.
* a column number of -1 means the (data) line number (not the file line
* number). splot 'file' using 1 is equivalent to
* splot 'file' using 0:-1:1
* column number -2 is the index. It was put in to kludge multi-branch
* fitting.
*
* 20/5/95 : accept 1.23d4 in place of e (but not in scanf string)
* : autoextend data line buffer and MAX_COLS
*
* 11/8/96 : add 'columns' -1 for suggested y value, and -2 for
* current index.
* using 1:-1:-2 and column(-1) are supported.
* $-1 and $-2 are not yet supported, because of the
* way the parser works
*
*/
/*}}} */
/* Daniel Sebald: added general binary 2d data support. (20 August 2004)
*/
#include "datafile.h"
#include "alloc.h"
#include "axis.h"
#include "binary.h"
#include "command.h"
#include "eval.h"
#include "gp_time.h"
#include "graphics.h"
#include "misc.h"
#include "parse.h"
#include "plot.h"
#include "util.h"
#ifdef BINARY_DATA_FILE
#include "breaders.h"
#endif
/* test to see if the end of an inline datafile is reached */
#define is_EOF(c) ((c) == 'e' || (c) == 'E')
/* is it a comment line? */
#define is_comment(c) ((c) && (strchr(df_commentschars, (c)) != NULL))
/*{{{ static fns */
#if 0 /* not used */
static int get_time_cols __PROTO((char *fmt));
static void mod_def_usespec __PROTO((int specno, int jump));
#endif
static int check_missing __PROTO((char *s));
static char *df_gets __PROTO((void));
static int df_tokenise __PROTO((char *s));
#ifdef BINARY_DATA_FILE
static float *df_read_matrix __PROTO((int *rows, int *columns));
#else
static float **df_read_matrix __PROTO((int *rows, int *columns));
#endif
static void plot_option_every __PROTO((void));
static void plot_option_index __PROTO((void));
static void plot_option_thru __PROTO((void));
static void plot_option_using __PROTO((int));
static TBOOLEAN valid_format __PROTO((const char *));
#ifdef EAM_DATASTRINGS
static void plot_ticlabel_using __PROTO((int));
static void df_parse_string_field __PROTO((char *, char *));
#endif
#ifdef EAM_HISTOGRAMS
static void add_key_entry __PROTO((char *temp_string, int df_datum));
#endif
/*}}} */
/*{{{ variables */
#ifdef EAM_DATASTRINGS
enum COLUMN_TYPE { CT_DEFAULT, CT_STRING, CT_KEYLABEL,
CT_XTICLABEL, CT_X2TICLABEL, CT_YTICLABEL, CT_Y2TICLABEL,
CT_ZTICLABEL, CT_CBTICLABEL };
#endif
/* public variables client might access */
int df_no_use_specs; /* how many using columns were specified */
#ifdef EAM_HISTOGRAMS
struct curve_points *df_current_plot; /* set before calling df_readline() */
#endif
int df_line_number;
int df_datum; /* suggested x value if none given */
int df_eof = 0;
AXIS_INDEX df_axis[MAXDATACOLS];
TBOOLEAN df_matrix = FALSE; /* indicates if data originated from a 2D or 3D format */
TBOOLEAN df_binary = FALSE; /* this is a binary file */
/* jev -- the 'thru' function --- NULL means no dummy vars active */
/* HBB 990829: moved this here, from command.c */
struct udft_entry ydata_func;
/* string representing missing values in ascii datafiles */
char *missing_val = NULL;
/* input field separator, NUL if whitespace is the separator */
char df_separator = '\0';
/* comments chars */
char *df_commentschars = 0;
/* If any 'inline data' are in use for the current plot, flag this */
TBOOLEAN plotted_data_from_stdin = FALSE;
/* Setting this allows the parser to recognize Fortran D or Q */
/* format constants in the input file. But it slows things down */
TBOOLEAN df_fortran_constants = FALSE;
/* 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 size_t max_line_len = 0;
#define DATA_LINE_BUFSIZ 160
static FILE *data_fp = NULL;
#if defined(PIPES)
static TBOOLEAN df_pipe_open = FALSE;
#endif
static TBOOLEAN mixed_data_fp = FALSE; /* inline data */
char *df_filename; /* name of data file */
#ifdef EAM_DATASTRINGS
static int df_no_tic_specs; /* ticlabel columns not counted in df_no_use_specs */
#define MAX_TOKEN_LENGTH 64
#endif
#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 */
struct use_spec_s use_spec[MAXDATACOLS];
static char df_format[MAX_LINE_LEN + 1];
static TBOOLEAN evaluate_inside_using = FALSE;
/* rather than three arrays which all grow dynamically, make one
* dynamic array of this structure
*/
typedef struct df_column_struct {
double datum;
enum {
DF_BAD, DF_GOOD
} good;
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 */
static int fast_columns; /* corey@cac optimization */
#ifdef EAM_DATASTRINGS
char *df_tokens[MAXDATACOLS]; /* filled in by df_tokenise */
#ifdef GP_STRING_VARS
static char *df_stringexpression[MAXDATACOLS] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL};
#endif
#define NO_COLUMN_HEADER (-99) /* some value that can never be a real column */
static int column_for_key_title = NO_COLUMN_HEADER;
static TBOOLEAN key_title_auto_col = FALSE;
static char df_key_title[MAX_TOKEN_LENGTH]; /* filled in from in 1st row by df_tokenise */
#endif
#ifdef BINARY_DATA_FILE
/* Binary *read* variables used by df_readbinary(). The difference between matrix
* binary and general binary is that matrix binary requires an extra first column
* and extra first row giving the sample coordinates. Furthermore, note that if
* ASCII matrix data is converted to floats (i.e., binary) then it really falls in
* the general binary class, not the matrix binary class.
*/
TBOOLEAN df_read_binary;
TBOOLEAN df_matrix_binary;
int df_plot_mode;
/* Define the following true of binary is to have it's own format string. */
#define BINARY_HAS_OWN_FORMAT_STRING 1
static int df_readascii __PROTO((double [], int));
static int df_readbinary __PROTO((double [], int));
static void initialize_use_spec __PROTO((void));
static void initialize_binary_vars __PROTO((void));
static void df_insert_scanned_use_spec __PROTO((int));
static void adjust_binary_use_spec __PROTO((void));
static void clear_binary_records __PROTO((df_records_type));
static void plot_option_binary_format __PROTO((void));
static void plot_option_binary __PROTO((TBOOLEAN, TBOOLEAN));
static void plot_option_array __PROTO((void));
static TBOOLEAN rotation_matrix_2D __PROTO((double R[][2], double));
static TBOOLEAN rotation_matrix_3D __PROTO((double P[][3], double *));
static int token2tuple __PROTO((double *, int));
static void df_determine_matrix_info __PROTO((FILE *));
static void df_swap_bytes_by_endianess __PROTO((char *, int, int));
typedef enum df_multivalue_type {
DF_DELTA,
DF_FLIP_AXIS,
DF_FLIP,
DF_SCAN,
DF_ORIGIN,
DF_CENTER,
DF_ROTATION,
DF_PERPENDICULAR,
DF_SKIP
} df_multivalue_type;
static void plot_option_multivalued __PROTO((df_multivalue_type,int));
char *df_endian[DF_ENDIAN_TYPE_LENGTH] = {
"little",
"pdp (middle)",
"swapped pdp (dimmle)",
"big"
};
#define SUPPORT_MIDDLE_ENDIAN 1
#if SUPPORT_MIDDLE_ENDIAN
/* To generate a swap, take the bit-wise complement of the lowest two bits. */
typedef enum df_byte_read_order_type {
DF_0123,
DF_1032,
DF_2301,
DF_3210
} df_byte_read_order_type;
/* First argument, this program's endianess. Second argument, file's endianess.
* Don't use directly. Use 'byte_read_order()' function instead.*/
static char df_byte_read_order_map[4][4] = {
{DF_0123, DF_1032, DF_2301, DF_3210},
{DF_1032, DF_0123, DF_1032, DF_2301},
{DF_2301, DF_1032, DF_0123, DF_1032},
{DF_3210, DF_2301, DF_1032, DF_0123}
};
static long long_0x2468 = 0x2468;
#define TEST_BIG_PDP ( (((char *)&long_0x2468)[0] < 3) ? DF_BIG_ENDIAN : DF_PDP_ENDIAN )
#define THIS_COMPILER_ENDIAN ( (((char *)&long_0x2468)[0] < 5) ? TEST_BIG_PDP : DF_LITTLE_ENDIAN )
#else /* ifdef BINARY_DATA_FILE */
typedef enum df_byte_read_order_type {
DF_01,
DF_10
} df_byte_read_order_type;
static int int_1 = 1;
#define THIS_COMPILER_ENDIAN ( ((char *)&int_1)[0] ? DF_LITTLE_ENDIAN : DF_BIG_ENDIAN )
#endif /* ifdef BINARY_DATA_FILE */
/* Argument is file's endianess type. */
static df_byte_read_order_type byte_read_order __PROTO((df_endianess_type));
/* Logical variables indicating information about data file. */
TBOOLEAN df_binary_file;
TBOOLEAN df_matrix_file;
int df_M_count;
int df_N_count;
int df_O_count;
/* Initially set to default and then possibly altered by command line. */
df_binary_file_record_struct *df_bin_record = 0;
/* Default settings. */
df_binary_file_record_struct *df_bin_record_default = 0;
/* Settings that are transferred to default upon reset. */
df_binary_file_record_struct df_bin_record_reset = {
{-1, 0, 0},
{1, 1, 1},
{1, 1, 1},
DF_TRANSLATE_DEFAULT,
{0, 0, 0},
0,
{0, 0, 1},
{DF_SCAN_POINT, DF_SCAN_LINE, DF_SCAN_PLANE},
FALSE,
{0, 0, 0},
{0, 0, 0},
{1, 1, 1},
{0, 0, 0},
DF_TRANSLATE_DEFAULT,
{0, 0, 0},
NULL
};
int df_max_num_bin_records = 0, df_num_bin_records, df_bin_record_count;
int df_max_num_bin_records_default = 0, df_num_bin_records_default;
typedef struct df_bin_filetype_table_struct {
char *extension;
void (*function)(void);
} df_bin_filetype_table_struct;
static void gpbin_filetype_function __PROTO((void));
static void raw_filetype_function __PROTO((void));
static void avs_filetype_function __PROTO((void));
df_bin_filetype_table_struct df_bin_filetype_table[] = {
{"gpbin", gpbin_filetype_function},
{"raw", raw_filetype_function},
{"rgb", raw_filetype_function},
{"bin", raw_filetype_function},
#if 1
{"avs", avs_filetype_function},
{"edf", edf_filetype_function},
{"ehf", edf_filetype_function},
#endif
{"auto", raw_filetype_function} /* "auto" is trapped, but if the actual file extension is "auto" then use raw. */
};
#define RAW_FILETYPE 1
/* Initially set to default and then possibly altered by command line. */
int df_bin_filetype;
df_endianess_type df_bin_file_endianess;
/* Default setting. */
int df_bin_filetype_default;
df_endianess_type df_bin_file_endianess_default;
/* Setting that is transferred to default upon reset. */
int df_bin_filetype_reset = -1;
#define DF_BIN_FILE_ENDIANESS_RESET THIS_COMPILER_ENDIAN
typedef struct df_bin_scan_table_2D_struct {
char *string;
df_sample_scan_type scan[3];
} df_bin_scan_table_2D_struct;
df_bin_scan_table_2D_struct df_bin_scan_table_2D[] = {
{"xy", {DF_SCAN_POINT, DF_SCAN_LINE, DF_SCAN_PLANE}},
{"yx", {DF_SCAN_LINE, DF_SCAN_POINT, DF_SCAN_PLANE}},
{"tr", {DF_SCAN_POINT, DF_SCAN_LINE, DF_SCAN_PLANE}},
{"rt", {DF_SCAN_LINE, DF_SCAN_POINT, DF_SCAN_PLANE}}
};
#define TRANSPOSE_INDEX 1
typedef struct df_bin_scan_table_3D_struct {
char *string;
df_sample_scan_type scan[3];
} df_bin_scan_table_3D_struct;
df_bin_scan_table_3D_struct df_bin_scan_table_3D[] = {
{"xyz", {DF_SCAN_POINT, DF_SCAN_LINE, DF_SCAN_PLANE}},
{"zxy", {DF_SCAN_LINE, DF_SCAN_PLANE, DF_SCAN_POINT}},
{"yzx", {DF_SCAN_PLANE, DF_SCAN_POINT, DF_SCAN_LINE}},
{"yxz", {DF_SCAN_LINE, DF_SCAN_POINT, DF_SCAN_PLANE}},
{"xzy", {DF_SCAN_POINT, DF_SCAN_PLANE, DF_SCAN_LINE}},
{"zyx", {DF_SCAN_PLANE, DF_SCAN_LINE, DF_SCAN_POINT}},
{"trz", {DF_SCAN_POINT, DF_SCAN_LINE, DF_SCAN_PLANE}},
{"ztr", {DF_SCAN_LINE, DF_SCAN_PLANE, DF_SCAN_POINT}},
{"rzt", {DF_SCAN_PLANE, DF_SCAN_POINT, DF_SCAN_LINE}},
{"rtz", {DF_SCAN_LINE, DF_SCAN_POINT, DF_SCAN_PLANE}},
{"tzr", {DF_SCAN_POINT, DF_SCAN_PLANE, DF_SCAN_LINE}},
{"zrt", {DF_SCAN_PLANE, DF_SCAN_LINE, DF_SCAN_POINT}}
};
/* Names for machine dependent field sizes. */
char *ch_names[] = {"char","schar","c"};
char *uc_names[] = {"uchar"};
char *sh_names[] = {"short"};
char *us_names[] = {"ushort"};
char *in_names[] = {"int","sint","i","d"};
char *ui_names[] = {"uint","u"};
char *lo_names[] = {"long","ld"};
char *ul_names[] = {"ulong","lu"};
char *fl_names[] = {"float","f"};
char *db_names[] = {"double","lf"};
/* Machine independent names. */
char *byte_names[] = {"int8","byte"};
char *ubyte_names[] = {"uint8","ubyte"};
char *word_names[] = {"int16","word"};
char *uword_names[] = {"uint16","uword"};
char *word2_names[] = {"int32"};
char *uword2_names[] = {"uint32"};
char *word4_names[] = {"int64"};
char *uword4_names[] = {"uint64"};
char *float_names[] = {"float32"};
char *float2_names[] = {"float64"};
typedef struct df_binary_details_struct {
char **name;
unsigned short no_names;
df_binary_type_struct type;
} df_binary_details_struct;
typedef struct df_binary_tables_struct {
df_binary_details_struct *group;
unsigned short group_length;
} df_binary_tables_struct;
df_binary_details_struct df_binary_details[] = {
{ch_names,sizeof(ch_names)/sizeof(ch_names[0]),{DF_CHAR,sizeof(char)}},
{uc_names,sizeof(uc_names)/sizeof(uc_names[0]),{DF_UCHAR,sizeof(unsigned char)}},
{sh_names,sizeof(sh_names)/sizeof(sh_names[0]),{DF_SHORT,sizeof(short)}},
{us_names,sizeof(us_names)/sizeof(us_names[0]),{DF_USHORT,sizeof(unsigned short)}},
{in_names,sizeof(in_names)/sizeof(in_names[0]),{DF_INT,sizeof(int)}},
{ui_names,sizeof(ui_names)/sizeof(ui_names[0]),{DF_UINT,sizeof(unsigned int)}},
{lo_names,sizeof(lo_names)/sizeof(lo_names[0]),{DF_LONG,sizeof(long)}},
{ul_names,sizeof(ul_names)/sizeof(ul_names[0]),{DF_ULONG,sizeof(unsigned long)}},
{fl_names,sizeof(fl_names)/sizeof(fl_names[0]),{DF_FLOAT,sizeof(float)}},
{db_names,sizeof(db_names)/sizeof(db_names[0]),{DF_DOUBLE,sizeof(double)}}
};
df_binary_details_struct df_binary_details_independent[] = {
{byte_names,sizeof(byte_names)/sizeof(byte_names[0]),{SIGNED_TEST(1),1}},
{ubyte_names,sizeof(ubyte_names)/sizeof(ubyte_names[0]),{UNSIGNED_TEST(1),1}},
{word_names,sizeof(word_names)/sizeof(word_names[0]),{SIGNED_TEST(2),2}},
{uword_names,sizeof(uword_names)/sizeof(uword_names[0]),{UNSIGNED_TEST(2),2}},
{word2_names,sizeof(word2_names)/sizeof(word2_names[0]),{SIGNED_TEST(4),4}},
{uword2_names,sizeof(uword2_names)/sizeof(uword2_names[0]),{UNSIGNED_TEST(4),4}},
{word4_names,sizeof(word4_names)/sizeof(word4_names[0]),{SIGNED_TEST(8),8}},
{uword4_names,sizeof(uword4_names)/sizeof(uword4_names[0]),{UNSIGNED_TEST(8),8}},
{float_names,sizeof(float_names)/sizeof(float_names[0]),{FLOAT_TEST(4),4}},
{float2_names,sizeof(float2_names)/sizeof(float2_names[0]),{FLOAT_TEST(8),8}}
};
int df_no_bin_cols; /* binary columns to read */
df_binary_tables_struct df_binary_tables[] = {
{df_binary_details,sizeof(df_binary_details)/sizeof(df_binary_details[0])},
{df_binary_details_independent,sizeof(df_binary_details_independent)/sizeof(df_binary_details_independent[0])}
};
/* Information about binary data structure, to be determined by the
* using and format options. This should be one greater than df_no_bin_cols.
*/
static df_column_bininfo_struct *df_column_bininfo = NULL; /* allocate space as needed */
static int df_max_bininfo_cols = 0; /* space allocated */
static const char *matrix_general_binary_conflict_msg
= "Conflict between some matrix binary and general binary keywords";
#endif
/*}}} */
/*{{{ static char *df_gets() */
static char *
df_gets()
{
int len = 0;
/* HBB 20000526: prompt user for inline data, if in interactive mode */
if (mixed_data_fp && interactive)
fputs("input data ('e' ends) > ", stderr);
if (!fgets(line, max_line_len, data_fp))
return NULL;
if (mixed_data_fp)
++inline_num;
for (;;) {
len += strlen(line + len);
if (len > 0 && line[len - 1] == '\n') {
/* we have read an entire text-file line.
* Strip the trailing linefeed and return
*/
line[len - 1] = 0;
return line;
}
/* 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 = gp_realloc(line, max_line_len *= 2, "datafile line buffer");
if (!fgets(line + len, max_line_len - len, data_fp))
return line; /* unexpected end of file, but we have something to do */
}
/* NOTREACHED */
return NULL;
}
/*}}} */
/*{{{ static int df_tokenise(s) */
static int
df_tokenise(char *s)
{
/* implement our own sscanf that takes 'missing' into account,
* and can understand fortran quad format
*/
TBOOLEAN in_string;
#ifdef EAM_DATASTRINGS
int i;
for (i = 0; iplot_style == HISTOGRAMS))
column_for_key_title = use_spec[0].column;
#endif
#endif
#define NOTSEP (*s != df_separator)
df_no_cols = 0;
while (*s) {
/* check store - double max cols or add 20, whichever is greater */
if (df_max_cols 1
&& (use_spec[1].column == dfncp1
|| (df_no_use_specs > 2
&& (use_spec[2].column == dfncp1
|| (df_no_use_specs > 3
&& (use_spec[3].column == dfncp1
|| (df_no_use_specs > 4
&& (use_spec[4].column
== dfncp1
|| df_no_use_specs > 5)
)
)
)
)
)
)
)
)
)
) {
#if (0)
/* This was the [slow] code used through version 4.0 */
count = sscanf(s, "%lf%n", &df_column[df_no_cols].datum, &used);
#else
/* Use strtod() because
* - it is faster than sscanf()
* - sscanf(... %n ...) may not be portable
* - it allows error checking
* - atof() does not return a count or new position
*/
char *next;
df_column[df_no_cols].datum = gp_strtod(s, &next);
used = next - s;
count = (used) ? 1 : 0;
#endif
} else {
/* skip any space at start of column */
/* HBB tells me that the cast must be to
* unsigned char instead of int. */
while (isspace((unsigned char) *s) && NOTSEP)
++s;
count = (*s && NOTSEP) ? 1 : 0;
/* skip chars to end of column */
used = 0;
if (df_separator != '\0' && in_string) {
do
++s;
while (*s && *s != '"');
in_string = FALSE;
}
while (!isspace((unsigned char) *s)
&& (*s != NUL) && NOTSEP)
++s;
}
/* it might be a fortran double or quad precision.
* 'used' is only safe if count is 1
*/
if (df_fortran_constants && count == 1 &&
(s[used] == 'd' || s[used] == 'D' ||
s[used] == 'q' || s[used] == 'Q')) {
/* HBB 20001221: avoid breaking parsing of time/date
* strings like 01Dec2000 that would be caused by
* overwriting the 'D' with an 'e'... */
char *endptr;
char save_char = s[used];
/* might be fortran double */
s[used] = 'e';
/* and try again */
df_column[df_no_cols].datum = gp_strtod(s, &endptr);
count = (endptr == s) ? 0 : 1;
s[used] = save_char;
}
df_column[df_no_cols].good = count == 1 ? DF_GOOD : DF_BAD;
#ifdef HAVE_ISNAN
if (isnan(df_column[df_no_cols].datum))
df_column[df_no_cols].good = DF_BAD;
#endif
}
++df_no_cols;
/* EAM - 19 Aug 2002 If we are in a quoted string, skip to end
* of quote */
if (in_string) {
do
s++;
while (*s && (unsigned char) *s != '"');
}
/* skip to 1st character past next separator */
if (df_separator != '\0') {
while (*s && NOTSEP)
++s;
if (*s == df_separator)
/* skip leading whitespace in next field */
do
++s;
while (*s && isspace((unsigned char) *s) && NOTSEP);
} else {
/* skip chars to end of column */
while ((!isspace((unsigned char) *s)) && (*s != '\0'))
++s;
/* skip spaces to start of next column */
while (isspace((unsigned char) *s))
++s;
}
}
return df_no_cols;
#undef NOTSEP
}
/*}}} */
#ifdef BINARY_DATA_FILE
/*{{{ static float *df_read_matrix() */
/* Reads a matrix from a text file and stores it as floats in allocated
* memory.
*
* IMPORTANT NOTE: The routine returns the memory pointer for that matrix,
* but does not retain the pointer. Maintenance of the memory is left to
* the calling code.
*/
static float *
df_read_matrix(int *rows, int *cols)
{
int max_rows = 0;
int c;
float *linearized_matrix = NULL;
int bad_data = 0;
char *s;
int index = 0;
*rows = 0;
*cols = 0;
for (;;) {
if (!(s = df_gets())) {
df_eof = 1;
/* NULL if we have not read anything yet */
return linearized_matrix;
}
while (isspace((unsigned char) *s))
++s;
if (!*s || is_comment(*s)) {
if (linearized_matrix)
return linearized_matrix;
else
continue;
}
if (mixed_data_fp && is_EOF(*s)) {
df_eof = 1;
return linearized_matrix;
}
c = df_tokenise(s);
if (!c)
return linearized_matrix;
if (*cols && c != *cols) {
/* its not regular */
if (linearized_matrix)
free(linearized_matrix);
int_error(NO_CARET, "Matrix does not represent a grid");
}
*cols = c;
++*rows;
if (*rows > max_rows) {
max_rows = GPMAX(2*max_rows,1);
linearized_matrix = gp_realloc(linearized_matrix,
*cols * max_rows * sizeof(float),
"df_matrix");
}
/* store data */
{
int i;
for (i = 0; i < c; ++i) {
if (i < firstpoint && df_column[i].good != DF_GOOD) {
/* It's going to be skipped anyhow, so... */
linearized_matrix[index++] = 0;
} else
linearized_matrix[index++] = (float) df_column[i].datum;
if (df_column[i].good != DF_GOOD) {
if (bad_data++ == 0)
int_warn(NO_CARET,"matrix contains missing or undefined values");
}
}
}
}
}
/*}}} */
#else /* BINARY_DATA_FILE */
/*{{{ static float **df_read_matrix() */
/* reads a matrix from a text file stores in same storage format as
* fread_matrix */
/* FIXME HBB 20001207: doesn't respect 'index' at all, even though it
* could, and probably should. */
static float **
df_read_matrix(int *rows, int *cols)
{
int max_rows = 0;
int c;
float **rmatrix = NULL;
char *s;
*rows = 0;
*cols = 0;
for (;;) {
if (!(s = df_gets())) {
df_eof = 1;
return rmatrix; /* NULL if we have not read anything yet */
}
while (isspace((unsigned char) *s))
++s;
if (!*s || is_comment(*s)) {
if (rmatrix)
return rmatrix;
else
continue;
}
if (mixed_data_fp && is_EOF(*s)) {
df_eof = 1;
return rmatrix;
}
c = df_tokenise(s);
if (!c)
return rmatrix;
if (*cols && c != *cols) {
/* its not regular */
int_error(NO_CARET, "Matrix does not represent a grid");
}
*cols = c;
if (*rows >= max_rows) {
rmatrix = gp_realloc(rmatrix,
(max_rows += 10) * sizeof(float *),
"df_matrix");
}
/* allocate a row and store data */
{
int i;
float *row = rmatrix[*rows] = gp_alloc(c * sizeof(float),
"df_matrix row");
for (i = 0; i < c; ++i) {
if (df_column[i].good != DF_GOOD && i >= firstpoint)
int_error(NO_CARET, "Bad number in matrix");
row[i] = (float) df_column[i].datum;
}
++*rows;
}
}
}
/*}}} */
#endif /* BINARY_DATA_FILE */
static void
initialize_use_spec()
{
int i;
df_no_use_specs = 0;
for (i = 0; i < MAXDATACOLS; ++i) {
use_spec[i].column = i + 1; /* default column */
#ifdef EAM_DATASTRINGS
use_spec[i].expected_type = CT_DEFAULT; /* no particular expectation */
#endif
if (use_spec[i].at) {
free_at(use_spec[i].at);
use_spec[i].at = NULL; /* no expression */
}
df_axis[i] = -1; /* no timefmt for this output column */
}
}
/*{{{ int df_open(char *file_name, max_using) */
/* open file, parsing using/thru/index stuff return number of using
* specs [well, we have to return something !]
*/
int
df_open(const char *cmd_filename, int max_using)
{
int name_token = c_token - 1;
TBOOLEAN duplication = FALSE;
TBOOLEAN set_index = FALSE, set_every = FALSE, set_thru = FALSE;
TBOOLEAN set_using = FALSE;
#ifdef BINARY_DATA_FILE
TBOOLEAN set_matrix = FALSE;
#endif
fast_columns = 1; /* corey@cac */
/*{{{ close file if necessary */
if (data_fp)
df_close();
/*}}} */
/*{{{ initialise static variables */
df_format[0] = NUL; /* no format string */
#ifdef EAM_DATASTRINGS
df_no_tic_specs = 0;
#endif
df_key_title[0] = '\0';
initialize_use_spec();
df_datum = -1; /* it will be preincremented before use */
df_line_number = 0; /* ditto */
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;
#ifdef BINARY_DATA_FILE
df_binary_file = df_matrix_file = FALSE;
#endif
df_eof = 0;
#ifdef EAM_DATASTRINGS
column_for_key_title = NO_COLUMN_HEADER;
key_title_auto_col = FALSE;
#endif
/*}}} */
assert(max_using -1) &&
!S_ISREG(statbuf.st_mode) && !S_ISFIFO(statbuf.st_mode)) {
os_error(name_token, "\"%s\" is not a regular file or pipe",
df_filename);
}
}
#endif /* HAVE_SYS_STAT_H */
#ifdef BINARY_DATA_FILE
if ((data_fp = loadpath_fopen(df_filename, df_binary_file ? "rb" : "r")) ==
#else
if ((data_fp = loadpath_fopen(df_filename, df_binary ? "rb" : "r")) ==
#endif
(FILE *) NULL) {
int_warn(NO_CARET, "Skipping unreadable file \"%s\"", df_filename);
df_eof = 1;
return DF_EOF;
}
}
/*}}} */
#ifdef BINARY_DATA_FILE
/* If the data is in binary matrix form, read in some values
* to determine the nubmer of columns and rows. If data is in
* ASCII matrix form, read in all the data to memory in preparation
* for using df_readbinary() routine.
*/
if (df_matrix_file)
df_determine_matrix_info(data_fp);
/* General binary, matrix binary and ASCII matrix all use the
* df_readbinary() routine.
*/
if (df_binary_file || df_matrix_file) {
df_read_binary = TRUE;
adjust_binary_use_spec();
} else
df_read_binary = FALSE;
/* Make information about whether the data forms a grid or not
* available to the outside world. */
df_matrix = (df_matrix_file
|| ((df_num_bin_records == 1)
&& ((df_bin_record[0].cart_dim[1] > 0)
|| (df_bin_record[0].scan_dim[1] > 0))));
/* Same idea, but try removing this one. I can't see why it is
* important for the rest of the program to know if if the data
* came from a binary file. (DJS 20 Aug 2004) */
df_binary = df_binary_file;
#endif /* BINARY_DATA_FILE */
return df_no_use_specs;
}
/*}}} */
/*{{{ void df_close() */
void
df_close()
{
int i;
/* paranoid - mark $n and column(n) as invalid */
df_no_cols = 0;
if (!data_fp)
return;
if (ydata_func.at) {
free_at(ydata_func.at);
ydata_func.at = NULL;
}
/*{{{ free any use expression storage */
for (i = 0; i < MAXDATACOLS; ++i)
if (use_spec[i].at) {
free_at(use_spec[i].at);
use_spec[i].at = NULL;
}
/*}}} */
if (!mixed_data_fp) {
#if defined(PIPES)
if (df_pipe_open) {
(void) pclose(data_fp);
df_pipe_open = FALSE;
} else
#endif /* PIPES */
(void) fclose(data_fp);
}
mixed_data_fp = FALSE;
data_fp = NULL;
}
/*}}} */
/*{{{ void df_showdata() */
/* display the current data file line for an error message
*/
void
df_showdata()
{
if (data_fp && df_filename && line) {
/* display no more than 77 characters */
fprintf(stderr, "%.77s%s\n%s:%d:", line,
(strlen(line) > 77) ? "..." : "",
df_filename, df_line_number);
}
}
/*}}} */
static void
plot_option_every()
{
struct value a;
fast_columns = 0; /* corey@cac */
/* 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(c_token, "Expected positive integer");
}
/* 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(c_token, "Expected positive integer");
}
if (equals(c_token, ":") && !equals(++c_token, ":")) {
firstpoint = (int) real(const_express(&a));
if (firstpoint < 0)
int_error(c_token, "Expected non-negative integer");
}
if (equals(c_token, ":") && !equals(++c_token, ":")) {
firstline = (int) real(const_express(&a));
if (firstline < 0)
int_error(c_token, "Expected non-negative integer");
}
if (equals(c_token, ":") && !equals(++c_token, ":")) {
lastpoint = (int) real(const_express(&a));
if (lastpoint < firstpoint)
int_error(c_token, "Last point must not be before first point");
}
if (equals(c_token, ":")) {
++c_token;
lastline = (int) real(const_express(&a));
if (lastline < firstline)
int_error(c_token, "Last line must not be before first line");
}
}
static void
plot_option_index()
{
struct value a;
#ifdef BINARY_DATA_FILE
if (df_binary_file && df_matrix_file)
int_error(c_token, "Binary matrix file format does not allow more than one surface per file");
#else
if (df_binary)
int_error(c_token, "Binary file format does not allow more than one surface per file");
#endif
++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(c_token, "Upper index should be bigger than lower index");
if (equals(c_token, ":")) {
++c_token;
df_index_step = (int) magnitude(const_express(&a));
if (df_index_step < 1)
int_error(c_token, "Index step must be positive");
}
} else
df_upper_index = df_lower_index;
}
static void
plot_option_thru()
{
c_token++;
strcpy(c_dummy_var[0], set_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;
}
static void
plot_option_using(int max_using)
{
int no_cols = 0; /* For general binary only. */
#ifdef BINARY_DATA_FILE
/* The filetype function may have set the using specs, so reset
* them before processing tokens. */
if (df_binary_file)
initialize_use_spec();
#endif
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(c_token, "Too many columns in using specification");
if (equals(c_token, ":")) {
/* empty specification - use default */
use_spec[df_no_use_specs].column = df_no_use_specs;
if (df_no_use_specs > no_cols)
no_cols = df_no_use_specs;
++df_no_use_specs;
/* do not increment c+token ; let while() find the : */
} else if (equals(c_token, "(")) {
fast_columns = 0; /* corey@cac */
dummy_func = NULL; /* no dummy variables active */
/* this will match ()'s: */
at_highest_column_used = NO_COLUMN_HEADER;
use_spec[df_no_use_specs].at = perm_at();
if (no_cols < at_highest_column_used)
no_cols = at_highest_column_used;
/* Catch at least the simplest case of 'autotitle columnhead' using an expression */
use_spec[df_no_use_specs++].column = at_highest_column_used;
#ifdef EAM_DATASTRINGS
/* FIXME EAM - It would be nice to handle these like any other */
/* internal function via perm_at() but there are problems. */
} else if (almost_equals(c_token, "xtic$labels")) {
plot_ticlabel_using(CT_XTICLABEL);
} else if (almost_equals(c_token, "x2tic$labels")) {
plot_ticlabel_using(CT_X2TICLABEL);
} else if (almost_equals(c_token, "ytic$labels")) {
plot_ticlabel_using(CT_YTICLABEL);
} else if (almost_equals(c_token, "y2tic$labels")) {
plot_ticlabel_using(CT_Y2TICLABEL);
} else if (almost_equals(c_token, "ztic$labels")) {
plot_ticlabel_using(CT_ZTICLABEL);
} else if (almost_equals(c_token, "cbtic$labels")) {
plot_ticlabel_using(CT_CBTICLABEL);
} else if (almost_equals(c_token, "key")) {
plot_ticlabel_using(CT_KEYLABEL);
#endif /* EAM_DATASTRINGS */
} else {
int col = (int) real(const_express(&a));
if (col < -2)
int_error(c_token, "Column must be >= -2");
use_spec[df_no_use_specs++].column = col;
if (col > no_cols)
no_cols = col;
}
} while (equals(c_token, ":") && ++c_token);
}
#ifdef BINARY_DATA_FILE
if (df_binary_file) {
/* If the highest user column number is greater than number of binary
* columns, set the unitialized columns binary info to that of the last
* specified column or the default.
*/
df_extend_binary_columns(no_cols);
}
#endif
if (!END_OF_COMMAND && isstring(c_token)) {
#ifdef BINARY_DATA_FILE
if (df_binary_file)
# if BINARY_HAS_OWN_FORMAT_STRING
int_error(NO_CARET, "Place format string with `binary` keyword, i.e., \"binary format='...'\"\n\t or use \"binary filetype=...\" for in-file format if supported");
# else
if (df_matrix_file)
int_error(c_token, matrix_general_binary_conflict_msg);
plot_option_binary_format();
# endif /* BINARY_HAS_OWN_FORMAT_STRING */
#else /* BINARY_DATA_FILE */
if (df_binary)
int_error(NO_CARET, "Format string meaningless with binary data");
#endif /* BINARY_DATA_FILE */
quote_str(df_format, c_token, MAX_LINE_LEN);
if (!valid_format(df_format))
int_error(c_token,
"Please use between 1 and 7 conversions, of type double (%%lf)");
c_token++; /* skip format */
} /* if (!EOC) */
}
#ifdef EAM_DATASTRINGS
static
void plot_ticlabel_using(int axis)
{
struct value a;
int col = 0;
c_token += 2;
#ifdef GP_STRING_VARS
/* FIXME: What we really want is a test for a constant expression as */
/* opposed to a dummy expression. This is similar to the problem with */
/* with parsing the first argument of the plot command itself. */
if (isanumber(c_token) || type_udv(c_token)==INTGR) {
col = (int) real(const_express(&a));
use_spec[df_no_use_specs+df_no_tic_specs].at = NULL;
} else {
use_spec[df_no_use_specs+df_no_tic_specs].at = perm_at();
fast_columns = 0; /* Force all columns to be evaluated */
col = 1; /* Redundant because of the above */
}
#else
col = (int) real(const_express(&a));
#endif
if (col < 1)
int_error(c_token, "ticlabels must come from a real column");
c_token++;
use_spec[df_no_use_specs+df_no_tic_specs].expected_type = axis;
use_spec[df_no_use_specs+df_no_tic_specs].column = col;
df_no_tic_specs++;
}
#endif /* EAM_DATASTRINGS */
/*{{{ int df_readline(v, max) */
int
df_readline(double v[], int max)
{
if (!data_fp)
return DF_EOF;
#ifdef BINARY_DATA_FILE
if (df_read_binary)
/* General binary, matrix binary or matrix ascii
* that's been converted to binary.
*/
return df_readbinary(v, max);
else
#endif
return df_readascii(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_readascii(double v[], int max)
{
char *s;
assert(data_fp != NULL);
assert(max_line_len); /* alloc-ed in df_open() */
assert(max 0 */
if ((column p_count = width;
this_iso->next = this_plot->iso_crvs;
this_plot->iso_crvs = this_iso;
this_plot->num_iso_read++;
}
free_matrix(dmatrix, 0, nr - 1, 0);
if (rt)
free_vector(rt, 0);
if (ct)
free_vector(ct, 0);
/* HBB 20001208: implement 'index' for matrix datafiles */
df_current_index ++;
return (nc);
}
/*}}} */
#else /* REPLACED BY df_readbinary() READING JUST THE FIRST ROW AND SAVING FOR LATER USE. */
char *read_error_msg = "Data file read error";
double df_matrix_corner[2][2]; /* First argument is corner, second argument is x (0) or y(1). */
float
df_read_a_float(FILE *fin) {
float fdummy;
if (fread(&fdummy, sizeof(fdummy), 1, fin) != 1) {
if (feof(fin))
int_error(NO_CARET, "Data file is empty");
else
int_error(NO_CARET, read_error_msg);
}
df_swap_bytes_by_endianess((char *)&fdummy, byte_read_order(df_bin_file_endianess), sizeof(fdummy));
return fdummy;
}
void
df_determine_matrix_info(FILE *fin)
{
if (df_binary_file) {
/* Binary matrix format. */
float fdummy;
int nc, nr;
long flength;
/* Read first value for number of columns. */
fdummy = df_read_a_float(fin);
nc = ((size_t) fdummy);
if (nc == 0)
int_error(NO_CARET, "Read grid of zero width");
else if (nc > 1e8)
int_error(NO_CARET, "Read grid width too large"); /* A sanity check on reading absurd numbers. */
/* Read second value for corner_0 x. */
fdummy = df_read_a_float(fin);
df_matrix_corner[0][0] = fdummy;
/* Read nc+1 value for corner_1 x. */
if (nc > 1) {
fseek(fin, (nc-2)*sizeof(float), SEEK_CUR);
fdummy = df_read_a_float(fin);
}
df_matrix_corner[1][0] = fdummy;
/* Read nc+2 value for corner_0 y. */
df_matrix_corner[0][1] = df_read_a_float(fin);
/* Compute length of file and number of columns. */
fseek(fin, 0L, SEEK_END);
flength = ftell(fin)/sizeof(float);
nr = flength/(nc + 1);
if (nr*(nc + 1) != flength)
int_error(NO_CARET, "File doesn't factorize into full matrix");
/* Read last value for corner_1 y */
fseek(fin, -(nc + 1)*sizeof(float), SEEK_END);
df_matrix_corner[1][1] = df_read_a_float(fin);
/* Set up scan information for df_readbinary(). */
df_bin_record[0].scan_dim[0] = nc;
df_bin_record[0].scan_dim[1] = nr;
/* Reset counter file pointer. */
fseek(fin, 0L, SEEK_SET);
} else {
/* ASCII matrix format, converted to binary memory format. */
static float *matrix = NULL;
int nr, nc;
/* Insurance against creating a matrix with df_read_matrix()
* and then erroring out through df_add_binary_records().
*/
if (matrix)
free(matrix);
/* Set important binary variables, then free memory for all default
* binary records and set number of records to 0. */
initialize_binary_vars();
clear_binary_records(DF_CURRENT_RECORDS);
/* Keep reading matrices until file is empty. */
while (1) {
if ((matrix = df_read_matrix(&nr, &nc)) != NULL) {
int index = df_num_bin_records;
/* *** Careful! Could error out in next step. "matrix" should
* be static and test next time. ***
*/
df_add_binary_records(1, DF_CURRENT_RECORDS);
df_bin_record[index].memory_data = (char *) matrix;
matrix = NULL;
df_bin_record[index].scan_dim[0] = nc;
df_bin_record[index].scan_dim[1] = nr;
df_bin_record[index].scan_dim[2] = 0;
df_bin_file_endianess = THIS_COMPILER_ENDIAN;
} else
break;
}
/* Data from file is now in memory. Make the rest of gnuplot think
* that the data stream has not yet reached the end of file.
*/
df_eof = 0;
}
}
#endif
/* 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(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 != DF_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(union argument *arg)
{
struct value a;
int column;
(void) arg; /* avoid -Wunused warning */
(void) pop(&a);
column = (int) real(&a);
if (!evaluate_inside_using)
int_error(c_token-1, "column() called from invalid context");
if (column == -2)
push(Ginteger(&a, df_current_index));
else if (column == -1)
push(Ginteger(&a, line_count));
else if (column == 0) /* $0 = df_datum */
push(Gcomplex(&a, (double) df_datum, 0.0));
else if (column < 1
|| column > df_no_cols
|| df_column[column - 1].good != DF_GOOD
) {
undefined = TRUE;
push(&a); /* any objection to this ? */
} else
push(Gcomplex(&a, df_column[column - 1].datum, 0.0));
}
/* Called from int_error() */
void
df_reset_after_error()
{
evaluate_inside_using = FALSE;
}
#ifdef GP_STRING_VARS
void
f_stringcolumn(union argument *arg)
{
struct value a;
int column;
(void) arg; /* avoid -Wunused warning */
(void) pop(&a);
column = (int) real(&a);
if (!evaluate_inside_using)
int_error(c_token-1, "stringcolumn() called from invalid context");
if (column < 1 || column > df_no_cols) {
undefined = TRUE;
push(&a); /* any objection to this ? */
} else {
static char temp_string[MAX_TOKEN_LENGTH];
df_parse_string_field(temp_string, df_column[column-1].position);
push(Gstring(&a, temp_string ));
}
}
#endif
/*{{{ void f_valid() */
void
f_valid(union argument *arg)
{
struct value a;
int column, good;
(void) arg; /* avoid -Wunused warning */
(void) pop(&a);
column = (int) magnitude(&a) - 1;
good = column >= 0
&& column < df_no_cols
&& df_column[column].good == DF_GOOD;
push(Ginteger(&a, good));
}
/*}}} */
/*{{{ void f_timecolumn() */
/* HBB NOTE 20050505: this job is excessively tricky. We have one
* timefmt string per axis. Back then, that was essentially the only
* possibility, but it now poses a severe limitation. For simple
* using specs, the time parsing format should be a function of the
* column number in the datafile, not of the axis the data will be
* used for. For extended using specs, the value to go on a given
* axis could conceivably be built from multiple time/date entries in
* the datafile, each with its own format. */
/* HBB FIXME 20050505: this really should take two arguments, at
* least. First, the datafile column number. Second either a timefmt
* string (variable), or an axis index. For now, we have to try to
* guess the right axis index */
void
f_timecolumn(union argument *arg)
{
struct value a;
int column, spec;
AXIS_INDEX whichaxis;
struct tm tm;
int limit = (df_no_use_specs ? df_no_use_specs : MAXDATACOLS);
(void) arg; /* avoid -Wunused warning */
(void) pop(&a);
column = (int) magnitude(&a); /* HBB 20050505: removed - 1*/
if (!evaluate_inside_using)
int_error(c_token-1, "timecolumn() called from invalid context");
/* try to match datafile column with spec field number */
whichaxis = FIRST_X_AXIS;
for (spec = 0; speclabels) {
/* The first text_label structure in the list is a place-holder */
df_current_plot->labels = gp_alloc(sizeof(text_label), "key entry");
memset(df_current_plot->labels, 0, sizeof(text_label));
df_current_plot->labels->tag = -1;
}
new_entry->text = gp_strdup(temp_string);
new_entry->tag = df_datum;
new_entry->font = NULL;
new_entry->next = df_current_plot->labels->next;
df_current_plot->labels->next = new_entry;
}
#endif
#endif /* EAM_DATASTRINGS */
#ifdef BINARY_DATA_FILE
/* Construct 2D rotation matrix. */
/* R - Matrix to construct. */
/* alpha - Rotation angle. */
/* return - TRUE means a translation is required. */
TBOOLEAN
rotation_matrix_2D(double R[][2], double alpha)
{
static double I[2][2] = {{1, 0},
{0, 1}};
#define ANGLE_TOLERANCE 0.001
if (fabs(alpha) < ANGLE_TOLERANCE) {
/* Zero angle. Unity rotation. */
memcpy(R, I, sizeof(I));
return FALSE;
} else {
R[0][0] = cos(alpha);
R[0][1] = -sin(alpha);
R[1][0] = sin(alpha);
R[1][1] = cos(alpha);
return TRUE;
}
}
/* Construct 3D rotation matrix. */
/* P - Matrix to construct. */
/* p - Pointer to perpendicular vector. */
/* return - TRUE means a translation is required. */
TBOOLEAN
rotation_matrix_3D(double P[][3], double *p)
{
static double I[3][3] = {{1, 0, 0},
{0, 1, 0},
{0, 0, 1}};
double scale, C1, C2;
#define x p[0]
#define y p[1]
#define z p[2]
C1 = sqrt(x*x + y*y + z*z);
C2 = sqrt(x*x + y*y);
/* ????? Is there a precision constant for doubles similar to what is in limits.h for other types? */
if ((C1 < 10e-10) || (C2 < (10e-5*C1))) {
/* Zero vector (invalid) || vector perpendiculat to x/y plane. Unity rotation. */
memcpy(P, I, sizeof(I));
return FALSE;
} else {
scale = 1.0/(C1*C2);
P[0][0] = x*z * scale;
P[0][1] = -y*C1 * scale;
P[0][2] = x*C2 * scale;
P[1][0] = y*z * scale;
P[1][1] = x*C1 * scale;
P[1][2] = y*C2 * scale;
P[2][0] = -C2*C2 * scale;
P[2][1] = 0;
P[2][2] = z*C2 * scale;
return TRUE;
}
#undef x
#undef y
#undef z
}
df_byte_read_order_type
byte_read_order (df_endianess_type file_endian)
{
/* Range limit file endianess to ensure that future file type function
* programmer doesn't incorrectly access array and cause segmentation
* fault unknowingly.
*/
return df_byte_read_order_map[THIS_COMPILER_ENDIAN][GPMIN(file_endian, DF_ENDIAN_TYPE_LENGTH-1)];
}
void
df_unset_datafile_binary(void)
{
clear_binary_records(DF_DEFAULT_RECORDS);
df_bin_filetype_default = df_bin_filetype_reset;
df_bin_file_endianess_default = DF_BIN_FILE_ENDIANESS_RESET;
}
void
df_set_datafile_binary()
{
c_token++;
if (END_OF_COMMAND)
int_error(c_token, "option expected");
clear_binary_records(DF_CURRENT_RECORDS);
/* Set current records to default in order to retain current default settings. */
if (df_bin_record_default) {
df_bin_filetype = df_bin_filetype_default;
df_bin_file_endianess = df_bin_file_endianess_default;
df_add_binary_records(df_num_bin_records_default, DF_CURRENT_RECORDS);
memcpy(df_bin_record, df_bin_record_default, df_num_bin_records*sizeof(df_binary_file_record_struct));
} else {
df_bin_filetype = df_bin_filetype_reset;
df_bin_file_endianess = DF_BIN_FILE_ENDIANESS_RESET;
df_add_binary_records(1, DF_CURRENT_RECORDS);
}
/* Process the binary tokens. */
df_set_plot_mode(MODE_QUERY);
plot_option_binary(FALSE, TRUE);
/* Copy the modified settings as the new default settings. */
df_bin_filetype_default = df_bin_filetype;
df_bin_file_endianess_default = df_bin_file_endianess;
clear_binary_records(DF_DEFAULT_RECORDS);
df_add_binary_records(df_num_bin_records, DF_DEFAULT_RECORDS);
memcpy(df_bin_record_default, df_bin_record, df_num_bin_records_default*sizeof(df_binary_file_record_struct));
}
void
gpbin_filetype_function(void)
{
/* Gnuplot binary. */
df_matrix_file = TRUE;
df_binary_file = TRUE;
}
void
raw_filetype_function(void)
{
/* No information in file, just data. */
df_matrix_file = FALSE;
df_binary_file = TRUE;
}
void
avs_filetype_function(void)
{
/* A very simple file format:
* 8 byte header (width and height, 4 bytes each), unknown endian
* followed by 4 bytes per pixel (alpha, red, green, blue).
*/
FILE *fp;
unsigned long M, N;
int read_order = 0;
/* open (header) file */
fp = loadpath_fopen(df_filename, "rb");
if (!fp)
os_error(NO_CARET, "Can't open data file \"%s\"", df_filename);
/* read header: it is only 8 bytes */
if (!fread(&M, 4, 1, fp))
os_error(NO_CARET, "Can't read first dimension in data file \"%s\"", df_filename);
if (M > 0xFFFF)
read_order = DF_3210;
df_swap_bytes_by_endianess((char *) &M, read_order, 4);
if (!fread(&N, 4, 1, fp))
os_error(NO_CARET, "Can't read second dimension in data file \"%s\"", df_filename);
df_swap_bytes_by_endianess((char *) &N, read_order, 4);
fclose(fp);
df_matrix_file = FALSE;
df_binary_file = TRUE;
df_bin_record[0].scan_skip[0] = 8;
df_bin_record[0].scan_dim[0] = M;
df_bin_record[0].scan_dim[1] = N;
df_bin_record[0].scan_dir[0] = 1;
df_bin_record[0].scan_dir[1] = -1;
df_bin_record[0].scan_generate_coord = TRUE;
df_bin_record[0].cart_scan[0] = DF_SCAN_POINT;
df_bin_record[0].cart_scan[1] = DF_SCAN_LINE;
/* The four components are 1 byte each. Treat as three components
with the first one ignored. */
df_extend_binary_columns(3);
df_set_read_type(1, DF_UCHAR); /* Each pixel component is 1 byte */
df_set_read_type(2, DF_UCHAR);
df_set_read_type(3, DF_UCHAR);
df_set_skip_before(1,1); /* Ignore the alpha component of 4-tuple */
df_no_use_specs = 3;
use_spec[0].column = 1;
use_spec[1].column = 2;
use_spec[2].column = 3;
}
static void
initialize_binary_vars()
{
/* Initialize for the df_readline() routine. */
df_bin_record_count = 0;
df_M_count = df_N_count = df_O_count = 0;
/* Set default binary data widths and skip paratemers. */
df_no_bin_cols = 0;
df_set_skip_before(1, 0);
/* Copy the default binary records to the active binary records. The number
* of records will always be at least one in case "record", "array",
* or "filetype" are not issued by the user.
*/
clear_binary_records(DF_CURRENT_RECORDS);
if (df_num_bin_records_default) {
df_bin_filetype = df_bin_filetype_default;
df_bin_file_endianess = df_bin_file_endianess_default;
df_add_binary_records(df_num_bin_records_default, DF_CURRENT_RECORDS);
memcpy(df_bin_record, df_bin_record_default, df_num_bin_records*sizeof(df_binary_file_record_struct));
} else {
df_bin_filetype = df_bin_filetype_reset;
df_bin_file_endianess = DF_BIN_FILE_ENDIANESS_RESET;
df_add_binary_records(1, DF_CURRENT_RECORDS);
}
}
static char *too_many_cols_msg = "Too many columns in using specification and implied sampling array";
/* Place a special marker in the using list to derive the x/y/z value
* from the appropriate dimensional counter.
*/
void
df_insert_scanned_use_spec(int uspec)
{
/* Place a special marker in the using list to derive the z value
* from the third dimensional counter, which will be zero.
*/
if (df_no_use_specs >= MAXDATACOLS)
int_error(NO_CARET, too_many_cols_msg);
else {
int j;
for (j=df_no_use_specs; j > uspec; j--)
use_spec[j] = use_spec[j - 1];
use_spec[uspec].column = (uspec == 2 ? DF_SCAN_PLANE : DF_SCAN_LINE);
/* The at portion is set to NULL here, but this doesn't mash
* a valid memory pointer because any valid memory pointers
* were copied to new locations in the previous for loop.
*/
use_spec[uspec].at = NULL; /* Not a bad memory pointer overwrite!! */
df_no_use_specs++;
}
}
/* Not the most elegant way of defining the default columns, but I prefer
* this to switch and conditional statements when there are so many styles.
*/
#define LAST_PLOT_STYLE 26
typedef struct df_bin_default_columns {
PLOT_STYLE plot_style;
short excluding_gen_coords; /* Number of columns of information excluding generated coordinates. */
short dimen_in_2d; /* Number of additional columns required (in 2D plot) if coordinates not generated. */
} df_bin_default_columns;
df_bin_default_columns default_style_cols[LAST_PLOT_STYLE + 1] = {
{LINES, 1, 1},
{POINTSTYLE, 1, 1},
{IMPULSES, 1, 1},
{LINESPOINTS, 1, 1},
{DOTS, 1, 1},
{XERRORBARS, 2, 1},
{YERRORBARS, 2, 1},
{XYERRORBARS, 3, 1},
{BOXXYERROR, 3, 1},
{BOXES, 1, 1},
{BOXERROR, 3, 1},
{STEPS, 1, 1},
{FSTEPS, 1, 1},
{HISTEPS, 1, 1},
{VECTOR, 2, 2},
{CANDLESTICKS, 4, 1},
{FINANCEBARS, 4, 1},
{XERRORLINES, 2, 1},
{YERRORLINES, 2, 1},
{XYERRORLINES, 3, 1},
{FILLEDCURVES, 1, 1},
{PM3DSURFACE, 1, 2},
#ifdef EAM_DATASTRINGS
{LABELPOINTS, 2, 1},
#endif
#ifdef EAM_HISTOGRAMS
{HISTOGRAMS, 1, 0},
#endif
#ifdef WITH_IMAGE
{IMAGE, 1, 2},
{RGBIMAGE, 3, 2}
#endif
};
static void
adjust_binary_use_spec()
{
char *nothing_known = "No default columns known for that plot style";
enum PLOT_STYLE plot_style;
unsigned ps_index;
int c_token_copy;
/* This may appear strange, but ASCII matrix is not the same
* format as gnuplot matrix binary. So, although the ASCII
* *file* may be matrix, it's data structure is similar to
* an M x N general binary file, i.e., no extra row and column
* for sample coordinates.
*/
df_matrix_binary = (df_matrix_file && df_binary_file);
c_token_copy = c_token;
for (; !END_OF_COMMAND; c_token++)
if (almost_equals(c_token, "w$ith"))
break;
if (!END_OF_COMMAND)
plot_style = get_style();
else
plot_style = LINES;
c_token = c_token_copy;
/* Determine index. */
for (ps_index = 0; ps_index < sizeof(default_style_cols)/sizeof(default_style_cols[0]); ps_index++) {
if (default_style_cols[ps_index].plot_style == plot_style)
break;
}
if (ps_index == sizeof(default_style_cols)/sizeof(default_style_cols[0]))
int_error(c_token_copy, nothing_known);
/* Matrix format is interpretted as always having three columns. */
if (df_matrix_file) {
if (df_no_bin_cols > 3)
int_error(NO_CARET, "Matrix data contains only three columns");
df_extend_binary_columns(3);
}
/* If nothing has been done to set the using specs, use the default using
* characteristics for the style.
*/
if (!df_no_use_specs) {
if (!df_matrix_file) {
int no_cols = default_style_cols[ps_index].excluding_gen_coords;
if (!no_cols)
int_error(c_token_copy, nothing_known);
/* If coordinates are generated, make sure this plot style allows it.
* Otherwise, add in the number of generated coordinates and add an
* extra column if using `splot`.
*/
if (df_num_bin_records && df_bin_record[0].scan_generate_coord) {
if (default_style_cols[ps_index].dimen_in_2d == 0)
int_error(c_token_copy, "Cannot generate coords for that plot style");
} else {
/* If there aren't generated coordinates, then add the
* amount of columns that would be generated.
*/
no_cols += default_style_cols[ps_index].dimen_in_2d;
if (df_plot_mode == MODE_SPLOT)
no_cols++;
}
assert(no_cols 2)
int_error(NO_CARET, "Plot style requires higher than two-dimensional sampling array");
else {
if ((df_bin_record[k].cart_dim[1] == 0) && (df_bin_record[k].scan_dim[1] == 0)) {
if (default_style_cols[ps_index].dimen_in_2d > 1)
int_error(NO_CARET, "Plot style requires higher than one-dimensional sampling array");
else {
/* Place a special marker in the using list to derive the y value
* from the second dimensional counter.
*/
df_insert_scanned_use_spec(1);
}
}
/* Place a special marker in the using list to derive the z value
* from the third dimensional counter.
*/
df_insert_scanned_use_spec(2);
}
}
}
}
}
}
char *equal_symbol_msg = "Equal ('=') symbol required";
static void
plot_option_binary(TBOOLEAN set_matrix, TBOOLEAN set_default)
{
#define MAX_FILE_EXT_LEN 10
char file_ext[MAX_FILE_EXT_LEN+1];
TBOOLEAN duplication = FALSE;
TBOOLEAN set_record = FALSE;
TBOOLEAN set_array = FALSE, set_dx = FALSE, set_dy = FALSE, set_dz = FALSE;
TBOOLEAN set_center = FALSE, set_origin = FALSE, set_skip = FALSE, set_endian = FALSE;
TBOOLEAN set_rotation = FALSE, set_perpendicular = FALSE;
TBOOLEAN set_flip = FALSE, set_noflip = FALSE;
TBOOLEAN set_flipx = FALSE, set_flipy = FALSE, set_flipz = FALSE;
TBOOLEAN set_scan = FALSE;
#if BINARY_HAS_OWN_FORMAT_STRING
TBOOLEAN set_format = FALSE;
#endif
/* Binary file type must be the first word in the command following `binary`" */
if (almost_equals(c_token, "file$type") || (df_bin_filetype >= 0)) {
int i;
/* Above keyword not part of pre-existing binary definition.
* So use general binary. */
if (set_matrix)
int_error(c_token, matrix_general_binary_conflict_msg);
df_matrix_file = FALSE;
if (almost_equals(c_token, "file$type")) {
c_token++;
#define EQUAL_SYMBOL_NOT_REQUIRED 0
#if EQUAL_SYMBOL_NOT_REQUIRED
/* Ignore or do not require equal symbol. */
if (equals(c_token, "=")) c_token++;
#else
/* else equal symbol. */
if (!equals(c_token, "="))
int_error(c_token, equal_symbol_msg);
c_token++;
#endif
copy_str(file_ext, c_token, MAX_FILE_EXT_LEN);
for (i=0; i < (sizeof(df_bin_filetype_table)/sizeof(df_bin_filetype_table_struct)); i++) {
if (!strcasecmp(file_ext, df_bin_filetype_table[i].extension)) {
df_bin_filetype = i;
break;
}
}
if (i == (sizeof(df_bin_filetype_table)/sizeof(df_bin_filetype_table_struct)))
int_error(c_token, "Unsupported file type");
c_token++;
}
/* This section of code entails being able to read various types of
* binary data files. Some decisions need to be made on exactly what
* type or how many kinds should be allowed. Perhaps some system
* similar to the terminal scheme can be devised so that volunteers
* can provide code to read their favorite file type.
*
* My suggestion for a good scheme would be to have short little
* routines that look into the file in question and pull from the
* header the necessary information to fill in the details about
* "record", "array", "skip", etc. Then just let the current gnuplot
* code continue on. If the data file in question has compressed
* or encoded data, perhaps the data could be uncompressed or decoded
* into an intermediate, temporary file. In that case, the file name
* could be changed to the temporary file name and, again, just let
* gnuplot continue. Other approaches are possible.
*/
if (!strcasecmp("auto", df_bin_filetype_table[df_bin_filetype].extension) && (df_plot_mode != MODE_QUERY)) {
int i;
char *ext_start = strrchr (df_filename, '.');
if (!ext_start)
df_bin_filetype = RAW_FILETYPE;
else {
strncpy(file_ext, (ext_start+1), MAX_FILE_EXT_LEN);
for (i=0; i < (sizeof(df_bin_filetype_table)/sizeof(df_bin_filetype_table_struct)); i++) {
if (!strcasecmp(file_ext, df_bin_filetype_table[i].extension)) {
df_bin_filetype = i;
break;
}
}
if (i == (sizeof(df_bin_filetype_table)/sizeof(df_bin_filetype_table_struct)))
int_error(c_token, "Unsupported file type");
}
}
/* Unless only querying settings, call the routine to prep binary data parameters. */
if (df_plot_mode != MODE_QUERY)
df_bin_filetype_table[df_bin_filetype].function();
/* Now, at this point anything that was filled in for "scan" should
* override the "cart" variables.
*/
for (i=0; i < df_num_bin_records; i++) {
int j;
/* Dimension */
if (df_bin_record[i].scan_dim[0] != df_bin_record_reset.scan_dim[0])
for (j=0; j < 3; j++)
df_bin_record[i].cart_dim[j] = 0;
/* Delta */
for (j=0; j < 3; j++)
if (df_bin_record[i].scan_delta[j] != 0.0) {
int k;
for (k=0; k < 3; k++)
if (df_bin_record[i].cart_scan[k] == (DF_SCAN_POINT - j))
df_bin_record[i].cart_delta[k] = 0;
}
/* Translation */
if (df_bin_record[i].scan_trans != DF_TRANSLATE_DEFAULT)
df_bin_record[i].cart_trans = DF_TRANSLATE_DEFAULT;
}
}
while (!END_OF_COMMAND) {
char origin_and_center_conflict_message[] = "Can specify `origin` or `center`, but not both";
/* look for record */
if (almost_equals(c_token, "rec$ord")) {
if (set_record) { duplication=TRUE; break; }
c_token++;
/* Above keyword not part of pre-existing binary definition. So use general binary. */
if (set_matrix)
int_error(c_token, matrix_general_binary_conflict_msg);
df_matrix_file = FALSE;
plot_option_array();
set_record = TRUE;
continue;
}
/* look for array */
if (almost_equals(c_token, "arr$ay")) {
if (set_array) { duplication=TRUE; break; }
c_token++;
/* Above keyword not part of pre-existing binary definition. So use general binary. */
if (set_matrix)
int_error(c_token, matrix_general_binary_conflict_msg);
df_matrix_file = FALSE;
plot_option_array();
{int i;
for (i = 0; i < df_num_bin_records; i++)
df_bin_record[i].scan_generate_coord = TRUE; /* Indicate that coordinate info should be generated by gnuplot code. */
}
set_array = TRUE;
continue;
}
/* deal with spacing between array points */
if (equals(c_token, "dx") || equals(c_token, "dt")) {
if (set_dx) { duplication=TRUE; break; }
c_token++;
plot_option_multivalued(DF_DELTA, 0);
if (!set_dy) {
int i;
for (i = 0; i < df_num_bin_records; i++)
df_bin_record[i].cart_delta[1] = df_bin_record[i].cart_delta[0];
}
if (!set_dz) {
int i;
for (i = 0; i < df_num_bin_records; i++)
df_bin_record[i].cart_delta[2] = df_bin_record[i].cart_delta[0];
}
set_dx = TRUE;
continue;
}
if (equals(c_token, "dy") || equals(c_token, "dr")) {
if (set_dy) { duplication=TRUE; break; }
if (!set_array && !df_bin_record)
int_error(c_token, "Must specify a sampling array size before indicating spacing in second dimension");
c_token++;
plot_option_multivalued(DF_DELTA, 1);
if (!set_dz) {
int i;
for (i = 0; i < df_num_bin_records; i++)
df_bin_record[i].cart_delta[2] = df_bin_record[i].cart_delta[1];
}
set_dy = TRUE;
continue;
}
if (equals(c_token, "dz")) {
int_error(c_token, "Currently not supporting three-dimensional sampling");
if (set_dz) { duplication=TRUE; break; }
if (!set_array && !df_bin_record)
int_error(c_token, "Must specify a sampling array size before indicating spacing in third dimension");
c_token++;
plot_option_multivalued(DF_DELTA, 2);
set_dz = TRUE;
continue;
}
/* deal with direction in which sampling increments */
if (equals(c_token, "flipx")) {
if (set_flipx) { duplication=TRUE; break; }
c_token++;
/* If no equal sign, then set flip true for all records. */
#if EQUAL_SYMBOL_NOT_REQUIRED
if (!equals(c_token, "=") && !isanumber(c_token)) {
#else
if (!equals(c_token, "=")) {
#endif
int i;
for (i = 0; i < df_num_bin_records; i++)
df_bin_record[i].cart_dir[0] = -1;
} else {
plot_option_multivalued(DF_FLIP_AXIS, 0);
}
set_flipx = TRUE;
continue;
}
if (equals(c_token, "flipy")) {
if (set_flipy) { duplication=TRUE; break; }
if (!set_array && !df_bin_record)
int_error(c_token, "Must specify a sampling array size before indicating flip in second dimension");
c_token++;
/* If no equal sign, then set flip true for all records. */
#if EQUAL_SYMBOL_NOT_REQUIRED
if (!equals(c_token, "=") && !isanumber(c_token)) {
#else
if (!equals(c_token, "=")) {
#endif
int i;
for (i = 0; i < df_num_bin_records; i++)
df_bin_record[i].cart_dir[1] = -1;
} else {
plot_option_multivalued(DF_FLIP_AXIS, 1);
}
set_flipy = TRUE;
continue;
}
if (equals(c_token, "flipz")) {
int_error(c_token, "Currently not supporting three-dimensional sampling");
if (set_flipz) { duplication=TRUE; break; }
if (!set_array && !df_bin_record)
int_error(c_token, "Must specify a sampling array size before indicating spacing in third dimension");
c_token++;
/* If no equal sign, then set flip true for all records. */
#if EQUAL_SYMBOL_NOT_REQUIRED
if (!equals(c_token, "=") && !isanumber(c_token)) {
#else
if (!equals(c_token, "=")) {
#endif
int i;
for (i=0; i < df_num_bin_records; i++)
df_bin_record[i].cart_dir[2] = -1;
} else {
plot_option_multivalued(DF_FLIP_AXIS, 2);
}
set_flipz = TRUE;
continue;
}
/* Deal with flipping data for individual records. */
if (equals(c_token, "flip")) {
if (set_flip) { duplication=TRUE; break; }
c_token++;
plot_option_multivalued(DF_FLIP, -1);
set_flip = TRUE;
continue;
}
/* Deal with flipping data for individual records. */
if (equals(c_token, "noflip")) {
if (set_noflip) { duplication=TRUE; break; }
c_token++;
plot_option_multivalued(DF_FLIP, 1);
set_noflip = TRUE;
continue;
}
/* Deal with manner in which dimensions are scanned from file. */
if (equals(c_token, "scan")) {
if (set_scan) { duplication=TRUE; break; }
c_token++;
plot_option_multivalued(DF_SCAN, 0);
set_scan = TRUE;
continue;
}
/* Deal with manner in which dimensions are scanned from file. */
if (almost_equals(c_token, "trans$pose")) {
int i;
if (set_scan) { duplication=TRUE; break; }
c_token++;
for (i=0; i < df_num_bin_records; i++)
memcpy(df_bin_record[i].cart_scan, df_bin_scan_table_2D[TRANSPOSE_INDEX].scan, sizeof(df_bin_record[0].cart_scan));
set_scan = TRUE;
continue;
}
/* deal with origin */
if (almost_equals(c_token, "orig$in")) {
if (set_center)
int_error(c_token, origin_and_center_conflict_message);
if (set_origin) { duplication=TRUE; break; }
c_token++;
plot_option_multivalued(DF_ORIGIN, df_plot_mode);
set_origin = TRUE;
continue;
}
/* deal with origin */
if (almost_equals(c_token, "cen$ter")) {
if (set_origin)
int_error(c_token, origin_and_center_conflict_message);
if (set_center) { duplication=TRUE; break; }
c_token++;
plot_option_multivalued(DF_CENTER, df_plot_mode);
set_center = TRUE;
continue;
}
/* deal with rotation angle */
if (almost_equals(c_token, "rot$ation") || almost_equals(c_token, "rot$ate")) {
if (set_rotation) { duplication=TRUE; break; }
c_token++;
plot_option_multivalued(DF_ROTATION, 0);
set_rotation = TRUE;
continue;
}
/* deal with rotation angle */
if (almost_equals(c_token, "perp$endicular")) {
if (df_plot_mode == MODE_PLOT)
int_error(c_token, "Key word `perpendicular` is not allowed with `plot` command");
if (set_perpendicular) { duplication=TRUE; break; }
c_token++;
plot_option_multivalued(DF_PERPENDICULAR, 0);
set_perpendicular = TRUE;
continue;
}
/* deal with number of bytes to skip before record */
if (almost_equals(c_token, "skip")) {
if (set_skip) { duplication=TRUE; break; }
c_token++;
plot_option_multivalued(DF_SKIP, 0);
set_skip = TRUE;
continue;
}
/* deal with byte order */
if (almost_equals(c_token, "end$ian")) {
if (set_endian) { duplication=TRUE; break; }
c_token++;
#if EQUAL_SYMBOL_NOT_REQUIRED
/* Ignore or do not require equal symbol. */
if (equals(c_token, "=")) c_token++;
#else
/* Require equal symbol. */
if (!equals(c_token, "="))
int_error(c_token, equal_symbol_msg);
c_token++;
#endif
if (almost_equals(c_token, "def$ault"))
df_bin_file_endianess = THIS_COMPILER_ENDIAN;
else if (equals(c_token, "swap") || equals(c_token, "swab"))
df_bin_file_endianess = (~df_bin_file_endianess)&3; /* complement and isolate lowest two bits */
else if (almost_equals(c_token, "lit$tle"))
df_bin_file_endianess = DF_LITTLE_ENDIAN;
else if (equals(c_token, "big"))
df_bin_file_endianess = DF_BIG_ENDIAN;
#if SUPPORT_MIDDLE_ENDIAN
else if (almost_equals(c_token, "mid$dle") || equals(c_token, "pdp"))
df_bin_file_endianess = DF_PDP_ENDIAN;
else
int_error(c_token, "Options are default, swap (swab), little, big, middle (pdp)");
#else
else
int_error(c_token, "Options are default, swap (swab), little, big");
#endif
c_token++;
set_endian = TRUE;
continue;
}
#if BINARY_HAS_OWN_FORMAT_STRING
/* deal with various types of binary files */
if (almost_equals(c_token, "form$at")) {
if (set_default)
int_error(c_token, "Sorry - default binary properties not fully implemented");
if (set_format) { duplication=TRUE; break; }
c_token++;
/* Format string not part of pre-existing binary definition. So use general binary. */
if (set_matrix)
int_error(c_token, matrix_general_binary_conflict_msg);
df_matrix_file = FALSE;
#if EQUAL_SYMBOL_NOT_REQUIRED
/* Ignore or do not require equal symbol. */
if (equals(c_token, "=")) c_token++;
#else
/* Require equal symbol. */
if (!equals(c_token, "="))
int_error(c_token, equal_symbol_msg);
c_token++;
#endif
if (isstring(c_token))
plot_option_binary_format();
else
int_error(c_token, "Expecting format string");
set_format = TRUE;
continue;
}
#endif
break; /* unknown option */
} /* while (!END_OF_COMMAND) */
if (duplication)
int_error(c_token, "Duplicated or contradicting arguments in datafile options");
}
void
df_add_binary_records(int num_records_to_add, df_records_type records_type)
{
int i;
int new_number;
df_binary_file_record_struct **bin_record;
int *num_bin_records;
int *max_num_bin_records;
if (records_type == DF_CURRENT_RECORDS) {
bin_record = &df_bin_record;
num_bin_records = &df_num_bin_records;
max_num_bin_records = &df_max_num_bin_records;
} else {
bin_record = &df_bin_record_default;
num_bin_records = &df_num_bin_records_default;
max_num_bin_records = &df_max_num_bin_records_default;
}
new_number = *num_bin_records + num_records_to_add;
if (new_number > *max_num_bin_records) {
*bin_record
= gp_realloc(*bin_record,
new_number * sizeof(df_binary_file_record_struct),
"binary file data records");
if (!*bin_record) {
*max_num_bin_records = 0;
int_error(c_token,
"Error assigning memory for binary file data records");
}
*max_num_bin_records = new_number;
}
for (i = 0; i < num_records_to_add; i++) {
memcpy(*bin_record + *num_bin_records,
&df_bin_record_reset,
sizeof(df_binary_file_record_struct));
(*num_bin_records)++;
}
}
static void
clear_binary_records(df_records_type records_type)
{
df_binary_file_record_struct *temp_bin_record;
int *temp_num_bin_records;
int i;
if (records_type == DF_CURRENT_RECORDS) {
temp_bin_record = df_bin_record;
temp_num_bin_records = &df_num_bin_records;
} else {
temp_bin_record = df_bin_record_default;
temp_num_bin_records = &df_num_bin_records_default;
}
for (i = 0; i < *temp_num_bin_records; i++) {
if (temp_bin_record[i].memory_data != NULL) {
free(temp_bin_record[i].memory_data);
temp_bin_record[i].memory_data = NULL;
}
}
*temp_num_bin_records = 0;
}
#define TUPLE_SEPARATOR_CHAR ":"
#define LEFT_TUPLE_CHAR "(" /* Parser problems with (#,#) considered complex. */
#define RIGHT_TUPLE_CHAR ")"
static void
plot_option_array(void)
{
/* Process command line definition of array. */
if (!END_OF_COMMAND) {
int number_of_records = 0;
char *token_string;
TBOOLEAN expecting_number;
int ival;
int i_dimension = 0;
#if EQUAL_SYMBOL_NOT_REQUIRED
/* Ignore or do not require equal symbol. */
if (equals(c_token, "="))
c_token++;
#else
/* Require equal symbol. */
if (!equals(c_token, "="))
int_error(c_token, equal_symbol_msg);
c_token++;
#endif
/* Set true in case user starts string with a comma. */
expecting_number = TRUE;
/* If the user has no space between 'x' or 'X' and number, the
* parser creates a single token x#. So, copy string and work
* with that rather than the tokens directly. Null terminate
* and point to empty string. */
c_token--;
df_format[0] = '\0';
token_string = df_format;
while (1) {
if (*token_string == '\0') {
c_token++;
if (END_OF_COMMAND) break;
copy_str(df_format, c_token, MAX_LINE_LEN);
token_string = df_format;
}
if (expecting_number
&& !(isdigit(*token_string)
|| !strncasecmp(token_string, "Inf", 3)))
break;
if (!strcmp(token_string, TUPLE_SEPARATOR_CHAR)) {
i_dimension = 0;
token_string++;
expecting_number = TRUE;
continue;
}
if ((*token_string=='x') || (*token_string=='X') ) {
i_dimension++;
if (i_dimension >= 2)
int_error(c_token,
"Currently do not support sampled array dimensions greater than 2");
expecting_number = TRUE;
token_string++;
continue;
}
if (!expecting_number
&& (isdigit(*token_string)
|| !strncasecmp(token_string, "Inf", 3))) {
#if 0
/* No dimension symbol required. */
i_dimension++;
if (i_dimension >= 2)
int_error(c_token, "Currently do not support sampled array dimensions greater than 2");
#else
/* Dimension symbol or comma required. */
int_error(c_token, "Use '" TUPLE_SEPARATOR_CHAR "' between records or 'x' between dimensions");
#endif
}
if (!isdigit(*token_string) && strncasecmp(token_string, "Inf", 3)) break;
/* Read number, add records if necessary, record number, advance past number. */
if (isdigit(*token_string)) {
sscanf(token_string,"%d",&ival);
while(isdigit(*token_string)) token_string++;
} else {
int_error(c_token, "Sorry - Inf keyword not implemented");
}
if (!i_dimension) {
number_of_records++;
if (number_of_records > df_num_bin_records)
df_add_binary_records(1, DF_CURRENT_RECORDS);
}
df_bin_record[df_num_bin_records - 1].cart_dim[i_dimension] = ival;
expecting_number = FALSE;
}
/* Don't allow ending while expecting a number. */
if (expecting_number)
int_error(c_token, "Missing a number");
}
}
/* Evaluate a tuple of up to specified dimension. */
int
token2tuple(double *tuple, int dimension)
{
if (equals(c_token, LEFT_TUPLE_CHAR)) {
TBOOLEAN expecting_number = TRUE;
int N = 0;
c_token++;
while (!END_OF_COMMAND) {
if (expecting_number) {
struct value a;
double x;
x = real(const_express(&a));
N++;
if (N 0)
df_bin_record[bin_record_count].cart_dir[0] = arg;
else
int_error(c_token, cannot_flip_msg);
}
if (strpbrk(df_format, "yY")) {
if (df_bin_record[bin_record_count].cart_dim[1] > 0)
df_bin_record[bin_record_count].cart_dir[1] = arg;
else
int_error(c_token, cannot_flip_msg);
}
if (strpbrk(df_format, "zZ")) {
if (df_bin_record[bin_record_count].cart_dim[2] > 0)
df_bin_record[bin_record_count].cart_dir[2] = arg;
else
int_error(c_token, cannot_flip_msg);
}
c_token++;
break;
case DF_SCAN: {
/* Set the method in which data is scanned from
* file. Compare against a set number of strings. */
int i;
if (!(df_bin_record[bin_record_count].cart_dim[0]
|| df_bin_record[bin_record_count].scan_dim[0])
|| !(df_bin_record[bin_record_count].cart_dim[1]
|| df_bin_record[bin_record_count].scan_dim[1]))
int_error(c_token, "\
Cannot alter scanning method for one-dimensional data");
else if (df_bin_record[bin_record_count].cart_dim[2]
|| df_bin_record[bin_record_count].scan_dim[2]) {
for (i = 0;
i < sizeof(df_bin_scan_table_3D)
/sizeof(df_bin_scan_table_3D_struct);
i++)
if (equals(c_token,
df_bin_scan_table_3D[i].string)) {
memcpy(df_bin_record[bin_record_count].cart_scan,
df_bin_scan_table_3D[i].scan,
sizeof(df_bin_record[0].cart_scan));
break;
}
if (i == sizeof(df_bin_scan_table_3D)
/sizeof(df_bin_scan_table_3D_struct))
int_error(c_token, "\
Improper scanning string. Try 3 character string for 3D data");
} else {
for (i = 0;
i < sizeof(df_bin_scan_table_2D)
/sizeof(df_bin_scan_table_2D_struct); i++)
if (equals(c_token,
df_bin_scan_table_2D[i].string)) {
memcpy(df_bin_record[bin_record_count].cart_scan,
df_bin_scan_table_2D[i].scan,
sizeof(df_bin_record[0].cart_scan));
break;
}
if (i == sizeof(df_bin_scan_table_2D)
/sizeof(df_bin_scan_table_2D_struct))
int_error(c_token, "\
Improper scanning string. Try 2 character string for 2D data");
}
/* Remove the file supplied scan direction. */
memcpy(df_bin_record[bin_record_count].scan_dir,
df_bin_record_reset.scan_dir,
sizeof(df_bin_record[0].scan_dir));
c_token++;
break;
}
case DF_SKIP:
/* Set the number of bytes to skip before reading
* record. */
df_bin_record[bin_record_count].scan_skip[0] = tuple[0];
if (df_bin_record[bin_record_count].scan_skip[0] != tuple[0])
int_error(c_token, "\
The number of bytes to skip must be an integer");
if (df_bin_record[bin_record_count].scan_skip[0] < 0)
int_error(c_token, "\
The number of bytes to skip must be positive");
break;
case DF_ORIGIN:
case DF_CENTER:
/* Set the origin or center of the image based upon
* the plot mode. */
if (type == DF_ORIGIN)
df_bin_record[bin_record_count].cart_trans
= DF_TRANSLATE_VIA_ORIGIN;
else
df_bin_record[bin_record_count].cart_trans
= DF_TRANSLATE_VIA_CENTER;
if (arg == MODE_PLOT) {
if (test_val != 2)
int_error(c_token, "\
Two-dimensional tuple required for 2D plot");
tuple[2] = 0.0;
} else if (arg == MODE_SPLOT) {
if (test_val != 3)
int_error(c_token, "\
Three-dimensional tuple required for 3D plot");
} else if (arg == MODE_QUERY) {
if (test_val != 3)
int_error(c_token, "\
Three-dimensional tuple required for setting binary parameters");
} else {
int_error(c_token, "\
Internal error (datafile.c): Unknown plot mode");
}
memcpy(df_bin_record[bin_record_count].cart_cen_or_ori,
tuple, sizeof(tuple));
break;
case DF_ROTATION:
/* Allow user to enter angle in terms of pi or degrees. */
if (equals(c_token, "pi")) {
tuple[0] *= M_PI;
c_token++;
} else if (almost_equals(c_token, "d$egrees")) {
tuple[0] *= M_PI/180;
c_token++;
}
/* Construct 2D rotation matrix. */
df_bin_record[bin_record_count].cart_alpha = tuple[0];
break;
case DF_PERPENDICULAR:
/* Make sure in three dimensional plotting mode before
* accepting the perpendicular vector for translation. */
if (test_val != 3)
int_error(c_token, "Three-dimensional tuple required");
/* Compare vector length against variable precision
* to determine if this is the null vector */
if ((tuple[0]*tuple[0]
+ tuple[1]*tuple[1]
+ tuple[2]*tuple[2]) < 100.*DBL_EPSILON)
int_error(c_token, "\
Perpendicular vector cannot be zero");
memcpy(df_bin_record[bin_record_count].cart_p,
tuple,
sizeof(tuple));
break;
default:
int_error(NO_CARET, "\
Internal error (datafile.c): Invalid comma separated type");
} /* switch() */
} else {
int_error(c_token, "Invalid numeric or tuple form");
}
if (equals(c_token, TUPLE_SEPARATOR_CHAR)) {
bin_record_count++;
c_token++;
} else
break;
} /* while(!EOC) */
return;
}
/* Set the 'bytes' to skip before column 'col'. */
void
df_set_skip_before(int col, int bytes)
{
assert(col > 0);
/* Check if we have room at least col columns */
if (col > df_max_bininfo_cols) {
df_column_bininfo = gp_realloc(df_column_bininfo,
col * sizeof(df_column_bininfo_struct),
"datafile columns binary information");
df_max_bininfo_cols = col;
}
df_column_bininfo[col-1].skip_bytes = bytes;
}
/* Set the column data type. */
void
df_set_read_type(int col, df_data_type type)
{
assert(col > 0);
/* Check if we have room at least col columns */
if (col > df_max_bininfo_cols) {
df_column_bininfo = gp_realloc(df_column_bininfo,
col * sizeof(df_column_bininfo_struct),
"datafile columns binary information");
df_max_bininfo_cols = col;
}
df_column_bininfo[col-1].column.read_type = type;
df_column_bininfo[col-1].column.read_size
= df_binary_details[type].type.read_size;
}
/* Get the column data type. */
df_data_type
df_get_read_type(int col)
{
assert(col > 0);
/* Check if we have room at least col columns */
if (col < df_max_bininfo_cols)
return(df_column_bininfo[col].column.read_type);
else
return -1;
}
/* Get the binary column data size. */
int
df_get_read_size(int col)
{
assert(col > 0);
/* Check if we have room at least col columns */
if (col < df_max_bininfo_cols)
return(df_column_bininfo[col].column.read_size);
else
return -1;
}
/* If the column number is greater than number of binary columns, set
* the unitialized columns binary info to that of the last specified
* column or the default if none were set. */
void
df_extend_binary_columns(int no_cols)
{
if (no_cols > df_no_bin_cols) {
int i;
df_data_type type;
if (df_no_bin_cols > 0)
type = df_column_bininfo[df_no_bin_cols-1].column.read_type;
else
type = DF_DEFAULT_TYPE;
for (i = no_cols; i > df_no_bin_cols; i--) {
df_set_skip_after(i, 0);
df_set_read_type(i, type);
}
df_no_bin_cols = no_cols;
}
}
/* Determine binary data widths from the `using` (or `binary`) format
* specification. */
void
plot_option_binary_format(void)
{
int prev_read_type = DF_DEFAULT_TYPE; /* Defaults when none specified. */
int i, no_fields = 0;
/* Copy the token for our own analysis. */
copy_str(df_format, c_token, MAX_LINE_LEN);
for (i=1;
df_format[i] != '\0'
&& df_format[i] != '\"'
&& df_format[i] != '\''
&& i