GitHub Viewer
/* GNUPLOT - eval.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.
]*/
#include "eval.h"
#include "syscfg.h"
#include "alloc.h"
#include "amos_airy.h" /* Airy functions from AMOS */
#include "complexfun.h"
#include "datafile.h"
#include "datablock.h"
#include "external.h" /* for f_calle */
#include "internal.h"
#include "libcerf.h"
#include "misc.h" /* for called_from() */
#include "specfun.h"
#include "standard.h"
#include "util.h"
#include "version.h"
#include "term_api.h"
#include "voxelgrid.h"
#include
#include
/* Internal prototypes */
static RETSIGTYPE fpe(int an_int);
/* Global variables exported by this module */
struct udvt_entry udv_pi = { NULL, "pi", {INTGR, {0} } };
struct udvt_entry *udv_I;
struct udvt_entry *udv_NaN;
/* first in linked list */
struct udvt_entry *first_udv = &udv_pi;
struct udft_entry *first_udf = NULL;
/* pointer to first udv users can delete */
struct udvt_entry **udv_user_head;
/* Various abnormal conditions during evaluation of an action table
* (the stored form of an expression) are signalled by setting
* undefined = TRUE.
* NB: A test for "if (undefined)" is only valid immediately
* following a call to evaluate_at() or eval_link_function().
*/
TBOOLEAN undefined;
enum int64_overflow overflow_handling = INT64_OVERFLOW_TO_FLOAT;
/* The stack this operates on */
static struct value stack[STACK_DEPTH];
static int s_p = -1; /* stack pointer */
#define top_of_stack stack[s_p]
static int jump_offset; /* to be modified by 'jump' operators */
/* The table of built-in functions */
/* These must strictly parallel enum operators in eval.h */
const struct ft_entry ft[] =
{
/* internal functions: */
{"push", f_push},
{"pushc", f_pushc},
{"pushd1", f_pushd1},
{"pushd2", f_pushd2},
{"pushd", f_pushd},
{"pop", f_pop},
{"call", f_call},
{"calln", f_calln},
{"sum", f_sum},
{"lnot", f_lnot},
{"bnot", f_bnot},
{"uminus", f_uminus},
{"nop", f_nop},
{"lor", f_lor},
{"land", f_land},
{"bor", f_bor},
{"xor", f_xor},
{"band", f_band},
{"eq", f_eq},
{"ne", f_ne},
{"gt", f_gt},
{"lt", f_lt},
{"ge", f_ge},
{"le", f_le},
{"leftshift", f_leftshift},
{"rightshift", f_rightshift},
{"plus", f_plus},
{"minus", f_minus},
{"mult", f_mult},
{"div", f_div},
{"mod", f_mod},
{"power", f_power},
{"factorial", f_factorial},
{"bool", f_bool},
{"dollars", f_dollars}, /* for usespec */
{"concatenate", f_concatenate}, /* for string variables only */
{"eqs", f_eqs}, /* for string variables only */
{"nes", f_nes}, /* for string variables only */
{"[]", f_range}, /* substring or array slice */
{"[]", f_index}, /* for array variables only */
{"||", f_cardinality}, /* for array variables only */
{"assign", f_assign}, /* assignment operator '=' */
{"eval", f_eval}, /* function block */
{"jump", f_jump},
{"jumpz", f_jumpz},
{"jumpnz", f_jumpnz},
{"jtern", f_jtern},
/* Placeholder for SF_START */
{"", NULL},
#ifdef HAVE_EXTERNAL_FUNCTIONS
{"", f_calle},
#endif
/* legal in using spec only */
{"column", f_column},
{"stringcolumn", f_stringcolumn}, /* for using specs */
{"strcol", f_stringcolumn}, /* shorthand form */
{"columnhead", f_columnhead},
{"columnheader", f_columnhead},
{"valid", f_valid},
{"timecolumn", f_timecolumn},
/* standard functions: */
{"real", f_real},
{"imag", f_imag},
{"arg", f_arg},
{"conj", f_conjg},
{"conjg", f_conjg},
{"sin", f_sin},
{"cos", f_cos},
{"tan", f_tan},
{"asin", f_asin},
{"acos", f_acos},
{"atan", f_atan},
{"atan2", f_atan2},
{"sinh", f_sinh},
{"cosh", f_cosh},
{"tanh", f_tanh},
{"EllipticK", f_ellip_first},
{"EllipticE", f_ellip_second},
{"EllipticPi", f_ellip_third},
{"int", f_int},
{"round", f_round},
{"abs", f_abs},
{"sgn", f_sgn},
{"sqrt", f_sqrt},
{"cbrt", f_cbrt},
{"exp", f_exp},
{"log10", f_log10},
{"log", f_log},
{"besi0", f_besi0},
{"besi1", f_besi1},
{"besin", f_besin},
{"besj0", f_besj0},
{"besj1", f_besj1},
{"besjn", f_besjn},
{"besy0", f_besy0},
{"besy1", f_besy1},
{"besyn", f_besyn},
{"erf", f_erf},
{"erfc", f_erfc},
{"gamma", f_gamma},
{"lgamma", f_lgamma},
{"ibeta", f_ibeta},
{"voigt", f_voigt},
{"rand", f_rand},
{"floor", f_floor},
{"ceil", f_ceil},
{"norm", f_normal},
{"inverf", f_inverse_erf},
{"invnorm", f_inverse_normal},
{"invigamma", f_inverse_igamma},
{"invibeta", f_inverse_ibeta},
{"asinh", f_asinh},
{"acosh", f_acosh},
{"atanh", f_atanh},
{"lambertw", f_lambertw}, /* HBB, from G.Kuhnle 20001107 */
{"airy", f_airy}, /* cephes library version */
#ifdef HAVE_AMOS
{"Ai", f_amos_Ai}, /* Amos version from libopenspecfun */
{"Bi", f_amos_Bi}, /* Amos version from libopenspecfun */
{"BesselI", f_amos_BesselI},/* Amos version from libopenspecfun */
{"BesselJ", f_amos_BesselJ},/* Amos version from libopenspecfun */
{"BesselK", f_amos_BesselK},/* Amos version from libopenspecfun */
{"BesselY", f_amos_BesselY},/* Amos version from libopenspecfun */
{"Hankel1", f_Hankel1}, /* Amos version from libopenspecfun */
{"Hankel2", f_Hankel2}, /* Amos version from libopenspecfun */
#endif
#ifdef HAVE_CEXINT
{"expint", f_amos_cexint}, /* Amos algorithm 683 from libamos */
#else
{"expint", f_expint}, /* Jim Van Zandt, 20101010 */
#endif
#ifdef HAVE_COMPLEX_FUNCS
{"igamma", f_Igamma}, /* Complex igamma(a,z) */
{"LambertW", f_LambertW}, /* Complex W(z,k) */
{"lnGamma", f_lnGamma}, /* Complex lnGamma(z) */
{"Sign", f_Sign}, /* Complex sign function */
{"zeta", f_zeta}, /* Riemann zeta function */
#else
{"igamma", f_igamma}, /* Jos van der Woude 1992 */
#endif
{"uigamma", f_uigamma}, /* upper incomplete gamma */
#ifdef HAVE_LIBCERF
{"cerf", f_cerf}, /* complex error function */
{"cdawson", f_cdawson}, /* complex Dawson's integral */
{"erfi", f_erfi}, /* imaginary error function */
{"VP", f_voigtp}, /* Voigt profile */
{"VP_fwhm", f_VP_fwhm}, /* Voigt profile full width at half maximum */
{"faddeeva", f_faddeeva}, /* Faddeeva rescaled complex error function "w_of_z" */
{"FresnelC", f_FresnelC}, /* Fresnel integral cosine term calculated from cerf */
{"FresnelS", f_FresnelS}, /* Fresnel integral sine term calculated from cerf */
#endif
{"SynchrotronF", f_SynchrotronF}, /* Synchrotron F */
{"tm_sec", f_tmsec}, /* time function */
{"tm_min", f_tmmin}, /* time function */
{"tm_hour", f_tmhour}, /* time function */
{"tm_mday", f_tmmday}, /* time function */
{"tm_mon", f_tmmon}, /* time function */
{"tm_year", f_tmyear}, /* time function */
{"tm_wday", f_tmwday}, /* time function */
{"tm_yday", f_tmyday}, /* time function */
{"tm_week", f_tmweek}, /* time function */
{"weekdate_iso", f_weekdate_iso},
{"weekdate_cdc", f_weekdate_cdc},
{"join", f_join}, /* create string from array */
{"sprintf", f_sprintf}, /* for string variables only */
{"gprintf", f_gprintf}, /* for string variables only */
{"strlen", f_strlen}, /* for string variables only */
{"strstrt", f_strstrt}, /* for string variables only */
{"substr", f_range}, /* for string variables only */
{"split", f_split}, /* for string variables only */
{"trim", f_trim}, /* for string variables only */
{"word", f_word}, /* for string variables only */
{"words", f_words}, /* implemented as word(s,-1) */
{"strftime", f_strftime}, /* time to string */
{"strptime", f_strptime}, /* string to time */
{"time", f_time}, /* get current time */
{"system", f_system}, /* "dynamic backtics" */
{"exist", f_exists}, /* exists("foo") replaces defined(foo) */
{"exists", f_exists}, /* exists("foo") replaces defined(foo) */
{"value", f_value}, /* retrieve value of variable known by name */
{"index", f_lookup}, /* retrieve index of array entry with known value */
{"hsv2rgb", f_hsv2rgb}, /* color conversion */
{"palette", f_palette}, /* palette color lookup */
{"rgbcolor", f_rgbcolor}, /* 32bit ARGB color lookup by name or string */
#ifdef VOXEL_GRID_SUPPORT
{"voxel", f_voxel}, /* extract value of single voxel */
#endif
{NULL, NULL}
};
/* Module-local variables: */
static JMP_BUF fpe_env;
/* Internal helper functions: */
static RETSIGTYPE
fpe(int an_int)
{
#if defined(MSDOS) && !defined(DJGPP)
/* thanks to lotto@wjh12.UUCP for telling us about this */
_fpreset();
#endif
(void) an_int; /* avoid -Wunused warning */
(void) signal(SIGFPE, (sigfunc) fpe);
undefined = TRUE;
LONGJMP(fpe_env, TRUE);
}
/* Exported functions */
/* First, some functions that help other modules use 'struct value' ---
* these might justify a separate module, but I'll stick with this,
* for now */
/* returns the real part of val */
double
real(struct value *val)
{
switch (val->type) {
case INTGR:
return ((double) val->v.int_val);
case CMPLX:
return (val->v.cmplx_val.real);
case STRING:
return (atof(val->v.string_val));
case NOTDEFINED:
return not_a_number();
default:
int_error(NO_CARET, "unknown type in real()");
}
/* NOTREACHED */
return ((double) 0.0);
}
/* returns the imag part of val */
double
imag(struct value *val)
{
switch (val->type) {
case INTGR:
return (0.0);
case CMPLX:
return (val->v.cmplx_val.imag);
case STRING:
/* This is where we end up if the user tries: */
/* x = 2; plot sprintf(format,x) */
int_warn(NO_CARET, "encountered a string when expecting a number");
int_error(NO_CARET, "Did you try to generate a file name using dummy variable x or y?");
case NOTDEFINED:
return not_a_number();
default:
int_error(NO_CARET, "unknown type in imag()");
}
/* NOTREACHED */
return ((double) 0.0);
}
/* returns the magnitude of val */
double
magnitude(struct value *val)
{
switch (val->type) {
case INTGR:
return (fabs((double)val->v.int_val));
case CMPLX:
{
/* The straightforward implementation sqrt(r*r+i*i)
* over-/underflows if either r or i is very large or very
* small. This implementation avoids over-/underflows from
* squaring large/small numbers whenever possible. It
* only over-/underflows if the correct result would, too.
* CAVEAT: sqrt(1+x*x) can still have accuracy
* problems. */
double abs_r = fabs(val->v.cmplx_val.real);
double abs_i = fabs(val->v.cmplx_val.imag);
double quotient;
if (abs_i == 0)
return abs_r;
if (abs_r > abs_i) {
quotient = abs_i / abs_r;
return abs_r * sqrt(1 + quotient*quotient);
} else {
quotient = abs_r / abs_i;
return abs_i * sqrt(1 + quotient*quotient);
}
}
default:
int_error(NO_CARET, "unknown type in magnitude()");
}
/* NOTREACHED */
return ((double) 0.0);
}
/* returns the angle of val */
double
angle(struct value *val)
{
switch (val->type) {
case INTGR:
return ((val->v.int_val >= 0) ? 0.0 : M_PI);
case CMPLX:
if (val->v.cmplx_val.imag == 0.0) {
if (val->v.cmplx_val.real >= 0.0)
return (0.0);
else
return (M_PI);
}
return (atan2(val->v.cmplx_val.imag,
val->v.cmplx_val.real));
default:
int_error(NO_CARET, "unknown type in angle()");
}
/* NOTREACHED */
return ((double) 0.0);
}
struct value *
Gcomplex(struct value *a, double realpart, double imagpart)
{
a->type = CMPLX;
a->v.cmplx_val.real = realpart;
a->v.cmplx_val.imag = imagpart;
return (a);
}
struct value *
Ginteger(struct value *a, intgr_t i)
{
a->type = INTGR;
a->v.int_val = i;
return (a);
}
struct value *
Gstring(struct value *a, char *s)
{
a->type = STRING;
a->v.string_val = s ? s : strdup("");
return (a);
}
/* The rationale for introducing this routine was that multiple call sites
* wanted to write a new value to a variable that might already have one.
* free_value() was intended to consider all possible previous value types
* and free attached memory for types that had any.
*
* Caveat: When freeing values popped from the evaluation stack,
* datablocks and permanent arrays must not be freed because these are
* calls by reference to a continuing global variable.
* So the caller must clear the type field before calling free_value.
*/
void
free_value(struct value *a)
{
switch (a->type) {
case INTGR:
case CMPLX:
break;
case STRING:
gpfree_string(a);
break;
case ARRAY:
gpfree_array(a);
break;
case DATABLOCK:
gpfree_datablock(a);
break;
case FUNCTIONBLOCK:
gpfree_functionblock(a);
break;
case VOXELGRID: /* Should not happen! */
default: /* INVALID_VALUE INVALID_NAME */
break;
}
a->type = NOTDEFINED;
}
/* It would be fatal to call gpfree_string with a->type = STRING if
* a->string_val has already been freed.
* Setting 'a->type' to NOTDEFINED makes subsequent calls safe.
*/
void
gpfree_string(struct value *a)
{
if (a->type == STRING) {
free(a->v.string_val);
a->type = NOTDEFINED;
}
}
void
gpfree_array(struct value *a)
{
int i;
int size;
if (a->type == ARRAY) {
size = a->v.value_array[0].v.int_val;
for (i=1; iv.value_array[i]));
free(a->v.value_array);
a->type = NOTDEFINED;
}
}
void
init_array( struct udvt_entry *array, int size )
{
struct value *A;
int i;
free_value(&array->udv_value);
array->udv_value.v.value_array = gp_alloc((size+1) * sizeof(t_value), "init_array");
array->udv_value.type = ARRAY;
A = array->udv_value.v.value_array;
A[0].v.int_val = size;
for (i = 0; i udv_name))
return (*udv_ptr);
udv_ptr = &((*udv_ptr)->next_udv);
}
*udv_ptr = (struct udvt_entry *)
gp_alloc(sizeof(struct udvt_entry), "value");
(*udv_ptr)->next_udv = NULL;
(*udv_ptr)->udv_name = gp_strdup(key);
(*udv_ptr)->udv_value.type = NOTDEFINED;
return (*udv_ptr);
}
struct udvt_entry *
get_udv_by_name(char *key)
{
struct udvt_entry *udv = first_udv;
while (udv) {
if (!strcmp(key, udv->udv_name))
return udv;
udv = udv->next_udv;
}
return NULL;
}
/* This doesn't really delete, it just marks the udv as undefined */
void
del_udv_by_name(char *key, TBOOLEAN wildcard)
{
struct udvt_entry *udv_ptr = *udv_user_head;
while (udv_ptr) {
/* Forbidden to delete GPVAL_* */
if (!strncmp(udv_ptr->udv_name,"GPVAL",5))
;
else if (!strncmp(udv_ptr->udv_name,"GNUTERM",7))
;
/* exact match */
else if (!wildcard && !strcmp(key, udv_ptr->udv_name)) {
if (called_from(udv_ptr->udv_name)) {
FPRINTF((stderr, "cannot self-delete %s", udv_ptr->udv_name));
break;
}
gpfree_vgrid(udv_ptr);
free_value(&(udv_ptr->udv_value));
udv_ptr->udv_value.type = NOTDEFINED;
break;
}
/* wildcard match: prefix matches */
else if ( wildcard && !strncmp(key, udv_ptr->udv_name, strlen(key)) ) {
if (called_from(udv_ptr->udv_name)) {
FPRINTF((stderr, "cannot self-delete %s", udv_ptr->udv_name));
break;
}
gpfree_vgrid(udv_ptr);
free_value(&(udv_ptr->udv_value));
udv_ptr->udv_value.type = NOTDEFINED;
/* no break - keep looking! */
}
udv_ptr = udv_ptr->next_udv;
}
}
#ifdef USE_WATCHPOINTS
struct udft_entry *
get_udf_by_token(int t_num)
{
struct udft_entry **udf_ptr = &first_udf;
while (*udf_ptr) {
if (equals(t_num, (*udf_ptr)->udf_name))
return *udf_ptr;
udf_ptr = &((*udf_ptr)->next_udf);
}
return NULL;
}
#endif
/* Clear (delete) all user defined functions */
void
clear_udf_list()
{
struct udft_entry *udf_ptr = first_udf;
struct udft_entry *udf_next;
while (udf_ptr) {
free(udf_ptr->udf_name);
free(udf_ptr->definition);
free_at(udf_ptr->at);
udf_next = udf_ptr->next_udf;
free(udf_ptr);
udf_ptr = udf_next;
}
first_udf = NULL;
}
static void update_plot_bounds(void);
static void fill_gpval_axis(AXIS_INDEX axis);
static void fill_gpval_sysinfo(void);
static void set_gpval_axis_sth_double(const char *prefix, AXIS_INDEX axis, const char *suffix, double value);
static void
set_gpval_axis_sth_double(const char *prefix, AXIS_INDEX axis, const char *suffix, double value)
{
struct udvt_entry *v;
char *cc, s[24];
sprintf(s, "%s_%s_%s", prefix, axis_name(axis), suffix);
for (cc=s; *cc; cc++)
*cc = toupper((unsigned char)*cc); /* make the name uppercase */
v = add_udv_by_name(s);
if (!v)
return; /* should not happen */
Gcomplex(&v->udv_value, value, 0);
}
static void
fill_gpval_axis(AXIS_INDEX axis)
{
const char *prefix = "GPVAL";
AXIS *ap = &axis_array[axis];
set_gpval_axis_sth_double(prefix, axis, "MIN", ap->min);
set_gpval_axis_sth_double(prefix, axis, "MAX", ap->max);
set_gpval_axis_sth_double(prefix, axis, "LOG", ap->base);
if (axis < POLAR_AXIS) {
set_gpval_axis_sth_double("GPVAL_DATA", axis, "MIN", ap->data_min);
set_gpval_axis_sth_double("GPVAL_DATA", axis, "MAX", ap->data_max);
}
}
/* Fill variable "var" visible by "show var" or "show var all" ("GPVAL_*")
* by the given value (string, integer, float, complex).
*/
void
fill_gpval_string(char *var, const char *stringvalue)
{
struct udvt_entry *v = add_udv_by_name(var);
if (!v)
return;
if (v->udv_value.type == STRING && !strcmp(v->udv_value.v.string_val, stringvalue))
return;
else
gpfree_string(&v->udv_value);
Gstring(&v->udv_value, gp_strdup(stringvalue));
}
void
fill_gpval_integer(char *var, intgr_t value)
{
struct udvt_entry *v = add_udv_by_name(var);
if (!v)
return;
Ginteger(&v->udv_value, value);
}
void
fill_gpval_float(char *var, double value)
{
struct udvt_entry *v = add_udv_by_name(var);
if (!v)
return;
Gcomplex(&v->udv_value, value, 0);
}
void
fill_gpval_complex(char *var, double areal, double aimag)
{
struct udvt_entry *v = add_udv_by_name(var);
if (!v)
return;
Gcomplex(&v->udv_value, areal, aimag);
}
/*
* Export axis bounds in terminal coordinates from previous plot.
* This allows offline mapping of pixel coordinates onto plot coordinates.
*/
static void
update_plot_bounds(void)
{
fill_gpval_float("GPVAL_TERM_XMIN", (double)axis_array[FIRST_X_AXIS].term_lower / term->tscale);
fill_gpval_float("GPVAL_TERM_XMAX", (double)axis_array[FIRST_X_AXIS].term_upper / term->tscale);
fill_gpval_float("GPVAL_TERM_YMIN", (double)axis_array[FIRST_Y_AXIS].term_lower / term->tscale);
fill_gpval_float("GPVAL_TERM_YMAX", (double)axis_array[FIRST_Y_AXIS].term_upper / term->tscale);
fill_gpval_integer("GPVAL_TERM_XSIZE", canvas.xright+1);
fill_gpval_integer("GPVAL_TERM_YSIZE", canvas.ytop+1);
fill_gpval_integer("GPVAL_TERM_SCALE", term->tscale);
/* May be useful for debugging font problems */
fill_gpval_integer("GPVAL_TERM_HCHAR", term->h_char);
fill_gpval_integer("GPVAL_TERM_VCHAR", term->v_char);
}
/*
* Put all the handling for GPVAL_* variables in this one routine.
* We call it from one of several contexts:
* 0: following a successful set/unset command
* 1: following a successful plot/splot
* 2: following an unsuccessful command (int_error)
* 3: program entry
* 4: explicit reset of error status
* 5: directory changed
* 6: X11 Window ID changed
*/
void
update_gpval_variables(int context)
{
/* These values may change during a plot command due to auto range */
if (context == 1) {
fill_gpval_axis(FIRST_X_AXIS);
fill_gpval_axis(FIRST_Y_AXIS);
fill_gpval_axis(SECOND_X_AXIS);
fill_gpval_axis(SECOND_Y_AXIS);
fill_gpval_axis(FIRST_Z_AXIS);
fill_gpval_axis(COLOR_AXIS);
fill_gpval_axis(T_AXIS);
fill_gpval_axis(U_AXIS);
fill_gpval_axis(V_AXIS);
fill_gpval_float("GPVAL_R_MIN", R_AXIS.min);
fill_gpval_float("GPVAL_R_MAX", R_AXIS.max);
fill_gpval_float("GPVAL_R_LOG", R_AXIS.base);
update_plot_bounds();
fill_gpval_integer("GPVAL_PLOT", is_3d_plot ? 0:1);
fill_gpval_integer("GPVAL_SPLOT", is_3d_plot ? 1:0);
fill_gpval_integer("GPVAL_VIEW_MAP", splot_map ? 1:0);
fill_gpval_float("GPVAL_VIEW_ROT_X", surface_rot_x);
fill_gpval_float("GPVAL_VIEW_ROT_Z", surface_rot_z);
fill_gpval_float("GPVAL_VIEW_SCALE", surface_scale);
fill_gpval_float("GPVAL_VIEW_ZSCALE", surface_zscale);
fill_gpval_float("GPVAL_VIEW_AZIMUTH", azimuth);
/* Screen coordinates of 3D rotational center and radius of the sphere */
/* in which x/y axes are drawn after 'set view equal xy[z]' */
fill_gpval_float("GPVAL_VIEW_XCENT",
(double)(canvas.xright+1 - xmiddle)/(double)(canvas.xright+1));
fill_gpval_float("GPVAL_VIEW_YCENT",
1.0 - (double)(canvas.ytop+1 - ymiddle)/(double)(canvas.ytop+1));
fill_gpval_float("GPVAL_VIEW_RADIUS",
0.5 * surface_scale * xscaler/(double)(canvas.xright+1));
return;
}
/* These are set after every "set" command, which is kind of silly */
/* because they only change after 'set term' 'set output' ... */
if (context == 0 || context == 2 || context == 3) {
/* This prevents a segfault if term==NULL, which can */
/* happen if set_terminal() exits via int_error(). */
if (!term)
fill_gpval_string("GPVAL_TERM", "unknown");
else
fill_gpval_string("GPVAL_TERM", (char *)(term->name));
fill_gpval_string("GPVAL_TERMOPTIONS", term_options);
fill_gpval_string("GPVAL_OUTPUT", (outstr) ? outstr : "");
fill_gpval_string("GPVAL_ENCODING", encoding_names[encoding]);
fill_gpval_string("GPVAL_MINUS_SIGN", minus_sign ? minus_sign : "-");
fill_gpval_string("GPVAL_MICRO", micro ? micro : "u");
fill_gpval_string("GPVAL_DEGREE_SIGN", degree_sign);
}
/* If we are called from int_error() then set the error state */
if (context == 2)
fill_gpval_integer("GPVAL_ERRNO", 1);
/* These initializations need only be done once, on program entry */
if (context == 3) {
struct udvt_entry *v = add_udv_by_name("GPVAL_VERSION");
char *tmp;
if (v && v->udv_value.type == NOTDEFINED)
Gcomplex(&v->udv_value, atof(gnuplot_version), 0);
v = add_udv_by_name("GPVAL_PATCHLEVEL");
if (v && v->udv_value.type == NOTDEFINED)
fill_gpval_string("GPVAL_PATCHLEVEL", gnuplot_patchlevel);
v = add_udv_by_name("GPVAL_COMPILE_OPTIONS");
if (v && v->udv_value.type == NOTDEFINED)
fill_gpval_string("GPVAL_COMPILE_OPTIONS", compile_options);
/* Start-up values */
fill_gpval_integer("GPVAL_MULTIPLOT", 0);
fill_gpval_integer("GPVAL_PLOT", 0);
fill_gpval_integer("GPVAL_SPLOT", 0);
tmp = get_terminals_names();
fill_gpval_string("GPVAL_TERMINALS", tmp);
free(tmp);
fill_gpval_string("GPVAL_ENCODING", encoding_names[encoding]);
/* Permanent copy of user-clobberable variables pi and NaN */
fill_gpval_float("GPVAL_pi", M_PI);
fill_gpval_float("GPVAL_NaN", not_a_number());
/* System information */
fill_gpval_sysinfo();
}
if (context == 3 || context == 4) {
fill_gpval_integer("GPVAL_ERRNO", 0);
fill_gpval_string("GPVAL_ERRMSG","");
fill_gpval_integer("GPVAL_SYSTEM_ERRNO", 0);
fill_gpval_string("GPVAL_SYSTEM_ERRMSG","");
}
/* GPVAL_PWD is unreliable. If the current directory becomes invalid,
* GPVAL_PWD does not reflect this. If this matters, the user can
* instead do something like MY_PWD = "`pwd`"
*/
if (context == 3 || context == 5) {
char *save_file = gp_alloc(PATH_MAX, "GPVAL_PWD");
int ierror = (GP_GETCWD(save_file, PATH_MAX) == NULL);
fill_gpval_string("GPVAL_PWD", ierror ? "" : save_file);
free(save_file);
}
if (context == 6) {
fill_gpval_integer("GPVAL_TERM_WINDOWID", current_x11_windowid);
}
}
/* System information is stored in GPVAL_BITS GPVAL_MACHINE GPVAL_SYSNAME */
#ifdef HAVE_UNAME
# include
#elif defined(_WIN32)
# include
#endif
void
fill_gpval_sysinfo()
{
/* For linux/posix systems with uname */
#ifdef HAVE_UNAME
struct utsname uts;
if (uname(&uts) < 0)
return;
fill_gpval_string("GPVAL_SYSNAME", uts.sysname);
fill_gpval_string("GPVAL_MACHINE", uts.machine);
/* For Windows systems */
#elif defined(_WIN32)
SYSTEM_INFO stInfo;
OSVERSIONINFO osvi;
char s[30];
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
snprintf(s, 30, "Windows_NT-%ld.%ld", osvi.dwMajorVersion, osvi.dwMinorVersion);
fill_gpval_string("GPVAL_SYSNAME", s);
GetSystemInfo(&stInfo);
switch (stInfo.wProcessorArchitecture)
{
case PROCESSOR_ARCHITECTURE_INTEL:
fill_gpval_string("GPVAL_MACHINE", "x86");
break;
case PROCESSOR_ARCHITECTURE_IA64:
fill_gpval_string("GPVAL_MACHINE", "ia64");
break;
case PROCESSOR_ARCHITECTURE_AMD64:
fill_gpval_string("GPVAL_MACHINE", "x86_64");
break;
default:
fill_gpval_string("GPVAL_MACHINE", "unknown");
}
#endif
/* For all systems */
fill_gpval_integer("GPVAL_BITS", 8 * sizeof(void *));
}
/* Callable wrapper for the words() internal function */
int
gp_words(char *string)
{
struct value a;
push(Gstring(&a, string));
f_words((union argument *)NULL);
pop(&a);
return a.v.int_val;
}
/* Callable wrapper for the word() internal function */
char *
gp_word(char *string, int i)
{
struct value a;
push(Gstring(&a, string));
push(Ginteger(&a, (intgr_t)i));
f_word((union argument *)NULL);
pop(&a);
return a.v.string_val;
}
/* New (version 5.5)
* The evaluation stack can now return an ARRAY value, but in order to do so
* without memory leaks or corruption it must make sure that the allocated
* content is distinct from the original content.
* I.e. {array A = ["foo"]; B = A; A = 0;} must leave a valid copy of "foo" in B[1].
*/
void
make_array_permanent(struct value *array)
{
struct value *copy;
int i, size;
/* If this array was generated by the evaluation stack it is safe to use
* but the temporary flag must be cleared.
*/
if (array->v.value_array[0].type == TEMP_ARRAY) {
array->v.value_array[0].type = NOTDEFINED;
return;
}
/* If it was a pre-existing array (no temporary flag) then we must make
* a clean copy of the whole thing.
*/
size = array->v.value_array[0].v.int_val;
copy = gp_alloc( (size+1) * sizeof(struct value), "array copy");
memcpy( copy, array->v.value_array, (size+1) * sizeof(struct value) );
for (i=0; iv.value_array = copy;
}
/* Extract a portion of the array full[N] into a new array slice[M]
* where M v.value_array;
struct value *slice;
int i,j;
/* Sanity checks */
if (beg < 1)
beg = 1;
if (end > array[0].v.int_val)
end = array[0].v.int_val;
if (end < beg)
beg = 1, end = 0;
slice = gp_alloc( (2 + end - beg) * sizeof(struct value), "array slice" );
slice[0].type = TEMP_ARRAY;
slice[0].v.int_val = 1 + end - beg;
for (i = beg, j = 1; i