#ifndef lint
static char *RCSid = "$Id: misc.c,v 1.15.2.5 2002/04/21 13:52:55 broeker Exp $";
#endif

/* GNUPLOT - misc.c */

/*[
 * Copyright 1986 - 1993, 1998   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.
]*/

#ifdef AMIGA_AC_5
# include <exec/types.h>
#endif /* AMIGA_AC_5 */

#include "plot.h"
#include "setshow.h"

extern int key_vpos, key_hpos, key_just;
extern int datatype[];
extern char timefmt[];

static void save_range __PROTO((FILE * fp, int axis, double min, double max, int autosc, char *text));
static void save_tics __PROTO((FILE * fp, int where, int axis, struct ticdef * tdef, TBOOLEAN rotate, char *text));
static void save_position __PROTO((FILE * fp, struct position * pos));
static void save_functions__sub __PROTO((FILE * fp));
static void save_variables__sub __PROTO((FILE * fp));
static int lf_pop __PROTO((void));
static void lf_push __PROTO((FILE * fp));
static int find_maxl_cntr __PROTO((struct gnuplot_contours * contours, int *count));


#define SAVE_NUM_OR_TIME(fp, x, axis) \
do{if (datatype[axis]==TIME) { \
  char s[80]; char *p; \
  putc('"', fp);   \
  gstrftime(s,80,timefmt,(double)(x)); \
  for(p=s; *p; ++p) {\
   if ( *p == '\t' ) fputs("\\t",fp);\
   else if (*p == '\n') fputs("\\n",fp); \
   else if ( *p > 126 || *p < 32 ) fprintf(fp,"\\%03o",*p);\
   else putc(*p, fp);\
  }\
  putc('"', fp);\
 } else {\
  fprintf(fp,"%#g",x);\
}} while(0)


/* State information for load_file(), to recover from errors
 * and properly handle recursive load_file calls
 */
typedef struct lf_state_struct LFS;
struct lf_state_struct {
    FILE *fp;			/* file pointer for load file */
    char *name;			/* name of file */
    TBOOLEAN interactive;	/* value of interactive flag on entry */
    TBOOLEAN do_load_arg_substitution;	/* likewise ... */
    int inline_num;		/* inline_num on entry */
    LFS *prev;			/* defines a stack */
    char *call_args[10];	/* args when file is 'call'ed instead of 'load'ed */
} *lf_head = NULL;		/* NULL if not in load_file */

/* these two could be in load_file, except for error recovery */
extern TBOOLEAN do_load_arg_substitution;
extern char *call_args[10];

/*
 * cp_alloc() allocates a curve_points structure that can hold 'num'
 * points.
 */
struct curve_points *
 cp_alloc(num)
int num;
{
    struct curve_points *cp;

    cp = (struct curve_points *) gp_alloc(sizeof(struct curve_points), "curve");
    cp->p_max = (num >= 0 ? num : 0);

    if (num > 0) {
	cp->points = (struct coordinate GPHUGE *)
	    gp_alloc(num * sizeof(struct coordinate), "curve points");
    } else
	cp->points = (struct coordinate GPHUGE *) NULL;
    cp->next_cp = NULL;
    cp->title = NULL;
    return (cp);
}


/*
 * cp_extend() reallocates a curve_points structure to hold "num"
 * points. This will either expand or shrink the storage.
 */
void cp_extend(cp, num)
struct curve_points *cp;
int num;
{

#if defined(DOS16) || defined(WIN16)
    /* Make sure we do not allocate more than 64k points in msdos since 
       * indexing is done with 16-bit int
       * Leave some bytes for malloc maintainance.
     */
    if (num > 32700)
	int_error("Array index must be less than 32k in msdos", NO_CARET);
#endif /* MSDOS */

    if (num == cp->p_max)
	return;

    if (num > 0) {
	if (cp->points == NULL) {
	    cp->points = (struct coordinate GPHUGE *)
		gp_alloc(num * sizeof(struct coordinate), "curve points");
	} else {
	    cp->points = (struct coordinate GPHUGE *)
		gp_realloc(cp->points, (unsigned long) num * sizeof(struct coordinate), "expanding curve points");
	}
	cp->p_max = num;
    } else {
	if (cp->points != (struct coordinate GPHUGE *) NULL)
	    free(cp->points);
	cp->points = (struct coordinate GPHUGE *) NULL;
	cp->p_max = 0;
    }
}

/*
 * cp_free() releases any memory which was previously malloc()'d to hold
 *   curve points (and recursively down the linked list).
 */
void cp_free(cp)
struct curve_points *cp;
{
    if (cp) {
	cp_free(cp->next_cp);
	if (cp->title)
	    free((char *) cp->title);
	if (cp->points)
	    free((char *) cp->points);
	free((char *) cp);
    }
}

/*
 * iso_alloc() allocates a iso_curve structure that can hold 'num'
 * points.
 */
struct iso_curve *
 iso_alloc(num)
int num;
{
    struct iso_curve *ip;
    ip = (struct iso_curve *) gp_alloc(sizeof(struct iso_curve), "iso curve");
    ip->p_max = (num >= 0 ? num : 0);
    if (num > 0) {
	ip->points = (struct coordinate GPHUGE *)
	    gp_alloc(num * sizeof(struct coordinate), "iso curve points");
    } else
	ip->points = (struct coordinate GPHUGE *) NULL;
    ip->next = NULL;
    return (ip);
}

/*
 * iso_extend() reallocates a iso_curve structure to hold "num"
 * points. This will either expand or shrink the storage.
 */
void iso_extend(ip, num)
struct iso_curve *ip;
int num;
{
    if (num == ip->p_max)
	return;

#if defined(DOS16) || defined(WIN16)
    /* Make sure we do not allocate more than 64k points in msdos since 
       * indexing is done with 16-bit int
       * Leave some bytes for malloc maintainance.
     */
    if (num > 32700)
	int_error("Array index must be less than 32k in msdos", NO_CARET);
#endif /* 16bit (Win)Doze */

    if (num > 0) {
	if (ip->points == NULL) {
	    ip->points = (struct coordinate GPHUGE *)
		gp_alloc(num * sizeof(struct coordinate), "iso curve points");
	} else {
	    ip->points = (struct coordinate GPHUGE *)
		gp_realloc(ip->points, num * sizeof(struct coordinate), "expanding curve points");
	}
	ip->p_max = num;
    } else {
	if (ip->points != (struct coordinate GPHUGE *) NULL)
	    free(ip->points);
	ip->points = (struct coordinate GPHUGE *) NULL;
	ip->p_max = 0;
    }
}

/*
 * iso_free() releases any memory which was previously malloc()'d to hold
 *   iso curve points.
 */
void iso_free(ip)
struct iso_curve *ip;
{
    if (ip) {
	if (ip->points)
	    free((char *) ip->points);
	free((char *) ip);
    }
}

/*
 * sp_alloc() allocates a surface_points structure that can hold 'num_iso_1'
 * iso-curves with 'num_samp_2' samples and 'num_iso_2' iso-curves with
 * 'num_samp_1' samples.
 * If, however num_iso_2 or num_samp_1 is zero no iso curves are allocated.
 */
struct surface_points *
 sp_alloc(num_samp_1, num_iso_1, num_samp_2, num_iso_2)
int num_samp_1, num_iso_1, num_samp_2, num_iso_2;
{
    struct surface_points *sp;

    sp = (struct surface_points *) gp_alloc(sizeof(struct surface_points), "surface");
    sp->next_sp = NULL;
    sp->title = NULL;
    sp->contours = NULL;
    sp->iso_crvs = NULL;
    sp->num_iso_read = 0;

    if (num_iso_2 > 0 && num_samp_1 > 0) {
	int i;
	struct iso_curve *icrv;

	for (i = 0; i < num_iso_1; i++) {
	    icrv = iso_alloc(num_samp_2);
	    icrv->next = sp->iso_crvs;
	    sp->iso_crvs = icrv;
	}
	for (i = 0; i < num_iso_2; i++) {
	    icrv = iso_alloc(num_samp_1);
	    icrv->next = sp->iso_crvs;
	    sp->iso_crvs = icrv;
	}
    } else
	sp->iso_crvs = (struct iso_curve *) NULL;

    return (sp);
}

/*
 * sp_replace() updates a surface_points structure so it can hold 'num_iso_1'
 * iso-curves with 'num_samp_2' samples and 'num_iso_2' iso-curves with
 * 'num_samp_1' samples.
 * If, however num_iso_2 or num_samp_1 is zero no iso curves are allocated.
 */
void sp_replace(sp, num_samp_1, num_iso_1, num_samp_2, num_iso_2)
struct surface_points *sp;
int num_samp_1, num_iso_1, num_samp_2, num_iso_2;
{
    int i;
    struct iso_curve *icrv, *icrvs = sp->iso_crvs;

    while (icrvs) {
	icrv = icrvs;
	icrvs = icrvs->next;
	iso_free(icrv);
    }
    sp->iso_crvs = NULL;

    if (num_iso_2 > 0 && num_samp_1 > 0) {
	for (i = 0; i < num_iso_1; i++) {
	    icrv = iso_alloc(num_samp_2);
	    icrv->next = sp->iso_crvs;
	    sp->iso_crvs = icrv;
	}
	for (i = 0; i < num_iso_2; i++) {
	    icrv = iso_alloc(num_samp_1);
	    icrv->next = sp->iso_crvs;
	    sp->iso_crvs = icrv;
	}
    } else
	sp->iso_crvs = (struct iso_curve *) NULL;
}

/*
 * sp_free() releases any memory which was previously malloc()'d to hold
 *   surface points.
 */
void sp_free(sp)
struct surface_points *sp;
{
    if (sp) {
	sp_free(sp->next_sp);
	if (sp->title)
	    free((char *) sp->title);
	if (sp->contours) {
	    struct gnuplot_contours *cntr, *cntrs = sp->contours;

	    while (cntrs) {
		cntr = cntrs;
		cntrs = cntrs->next;
		free(cntr->coords);
		free(cntr);
	    }
	}
	if (sp->iso_crvs) {
	    struct iso_curve *icrv, *icrvs = sp->iso_crvs;

	    while (icrvs) {
		icrv = icrvs;
		icrvs = icrvs->next;
		iso_free(icrv);
	    }
	}
	free((char *) sp);
    }
}


/*
 *  functions corresponding to the arguments of the GNUPLOT `save` command
 */
void save_functions(fp)
FILE *fp;
{
    if (fp) {
	show_version(fp);		/* I _love_ information written */
	save_functions__sub(fp);	/* at the top and the end of an */
	fputs("#    EOF\n", fp);	/* human readable ASCII file.   */
	(void) fclose(fp);	/*                        (JFi) */
    } else
	os_error("Cannot open save file", c_token);
}


void save_variables(fp)
FILE *fp;
{
    if (fp) {
	show_version(fp);
	save_variables__sub(fp);
	fputs("#    EOF\n", fp);
	(void) fclose(fp);
    } else
	os_error("Cannot open save file", c_token);
}


void save_set(fp)
FILE *fp;
{
    if (fp) {
	show_version(fp);
	save_set_all(fp);
	fputs("#    EOF\n", fp);
	(void) fclose(fp);
    } else
	os_error("Cannot open save file", c_token);
}


void save_all(fp)
FILE *fp;
{
    if (fp) {
	show_version(fp);
	save_set_all(fp);
	save_functions__sub(fp);
	save_variables__sub(fp);
	fprintf(fp, "%s\n", replot_line);
        if (wri_to_fil_last_fit_cmd(NULL)) {
           fputs("## ", fp);
           wri_to_fil_last_fit_cmd(fp);
           putc('\n', fp);
        }
	fputs("#    EOF\n", fp);
	(void) fclose(fp);
    } else
	os_error("Cannot open save file", c_token);
}

/*
 *  auxiliary functions
 */

static void save_functions__sub(fp)
FILE *fp;
{
    register struct udft_entry *udf = first_udf;

    while (udf) {
	if (udf->definition) {
	    fprintf(fp, "%s\n", udf->definition);
	}
	udf = udf->next_udf;
    }
}

static void save_variables__sub(fp)
FILE *fp;
{
    register struct udvt_entry *udv = first_udv->next_udv;	/* always skip pi */

    while (udv) {
	if (!udv->udv_undef) {
	    fprintf(fp, "%s = ", udv->udv_name);
	    disp_value(fp, &(udv->udv_value));
	    (void) putc('\n', fp);
	}
	udv = udv->next_udv;
    }
}

void save_set_all(fp)
FILE *fp;
{
    struct text_label *this_label;
    struct arrow_def *this_arrow;
    struct linestyle_def *this_linestyle;
    char str[MAX_LINE_LEN+1];

    /* opinions are split as to whether we save term and outfile
     * as a compromise, we output them as comments !
     */
    if (term)
	fprintf(fp, "# set terminal %s %s\n", term->name, term_options);
    else
	fputs("# set terminal unknown\n", fp);

    if (outstr)
	fprintf(fp, "# set output '%s'\n", outstr);
    else
	fputs("# set output\n", fp);

    fprintf(fp, "\
set %sclip points\n\
set %sclip one\n\
set %sclip two\n\
set bar %f\n",
	    (clip_points) ? "" : "no",
	    (clip_lines1) ? "" : "no",
	    (clip_lines2) ? "" : "no",
	    bar_size);

    if (draw_border)
	/* HBB 980609: handle border linestyle, too */
	fprintf(fp, "set border %d lt %d lw %.3f\n", draw_border, border_lp.l_type + 1, border_lp.l_width);
    else
	fprintf(fp, "set noborder\n");

    fprintf(fp, "\
set xdata%s\n\
set ydata%s\n\
set zdata%s\n\
set x2data%s\n\
set y2data%s\n",
	    datatype[FIRST_X_AXIS] == TIME ? " time" : "",
	    datatype[FIRST_Y_AXIS] == TIME ? " time" : "",
	    datatype[FIRST_Z_AXIS] == TIME ? " time" : "",
	    datatype[SECOND_X_AXIS] == TIME ? " time" : "",
	    datatype[SECOND_Y_AXIS] == TIME ? " time" : "");

    if (boxwidth < 0.0)
	fputs("set boxwidth\n", fp);
    else
	fprintf(fp, "set boxwidth %g\n", boxwidth);
    if (dgrid3d)
	fprintf(fp, "set dgrid3d %d,%d, %d\n",
		dgrid3d_row_fineness,
		dgrid3d_col_fineness,
		dgrid3d_norm_value);

    fprintf(fp, "set dummy %s,%s\n",dummy_var[0], dummy_var[1]);
    fprintf(fp, "set format x \"%s\"\n", conv_text(str, xformat));
    fprintf(fp, "set format y \"%s\"\n", conv_text(str, yformat));
    fprintf(fp, "set format x2 \"%s\"\n", conv_text(str, x2format));
    fprintf(fp, "set format y2 \"%s\"\n", conv_text(str, y2format));
    fprintf(fp, "set format z \"%s\"\n", conv_text(str, zformat));
    fprintf(fp, "set angles %s\n",
	    (angles_format == ANGLES_RADIANS) ? "radians" : "degrees");

    if (work_grid.l_type == 0)
	fputs("set nogrid\n", fp);
    else {
	if (polar_grid_angle)	/* set angle already output */
	    fprintf(fp, "set grid polar %f\n", polar_grid_angle / ang2rad);
	else
	    fputs("set grid nopolar\n", fp);
	fprintf(fp, "set grid %sxtics %sytics %sztics %sx2tics %sy2tics %smxtics %smytics %smztics %smx2tics %smy2tics lt %d lw %.3f, lt %d lw %.3f\n",
		work_grid.l_type & GRID_X ? "" : "no",
		work_grid.l_type & GRID_Y ? "" : "no",
		work_grid.l_type & GRID_Z ? "" : "no",
		work_grid.l_type & GRID_X2 ? "" : "no",
		work_grid.l_type & GRID_Y2 ? "" : "no",
		work_grid.l_type & GRID_MX ? "" : "no",
		work_grid.l_type & GRID_MY ? "" : "no",
		work_grid.l_type & GRID_MZ ? "" : "no",
		work_grid.l_type & GRID_MX2 ? "" : "no",
		work_grid.l_type & GRID_MY2 ? "" : "no",
		grid_lp.l_type + 1, grid_lp.l_width,
		mgrid_lp.l_type + 1, mgrid_lp.l_width);
    }
    fprintf(fp, "set key title \"%s\"\n", conv_text(str, key_title));
    switch (key) {
    case -1:{
	    fputs("set key", fp);
	    switch (key_hpos) {
	    case TRIGHT:
		fputs(" right", fp);
		break;
	    case TLEFT:
		fputs(" left", fp);
		break;
	    case TOUT:
		fputs(" out", fp);
		break;
	    }
	    switch (key_vpos) {
	    case TTOP:
		fputs(" top", fp);
		break;
	    case TBOTTOM:
		fputs(" bottom", fp);
		break;
	    case TUNDER:
		fputs(" below", fp);
		break;
	    }
	    break;
	}
    case 0:
	fputs("set nokey\n", fp);
	break;
    case 1:
	fputs("set key ", fp);
	save_position(fp, &key_user_pos);
	break;
    }
    if (key) {
	fprintf(fp, " %s %sreverse box linetype %d linewidth %.3f samplen %g spacing %g width %g\n",
		key_just == JLEFT ? "Left" : "Right",
		key_reverse ? "" : "no",
		key_box.l_type + 1, key_box.l_width, key_swidth, key_vert_factor, key_width_fix);
    }
    fputs("set nolabel\n", fp);
    for (this_label = first_label; this_label != NULL;
	 this_label = this_label->next) {
	fprintf(fp, "set label %d \"%s\" at ",
		this_label->tag,
		conv_text(str, this_label->text));
	save_position(fp, &this_label->place);

	switch (this_label->pos) {
	case LEFT:
	    fputs(" left", fp);
	    break;
	case CENTRE:
	    fputs(" centre", fp);
	    break;
	case RIGHT:
	    fputs(" right", fp);
	    break;
	}
	fprintf(fp, " %srotate", this_label->rotate ? "" : "no");
	if ((this_label->font)[0] != NUL)
	    fprintf(fp, " font \"%s\"", this_label->font);
	/* Entry font added by DJL */
	fputc('\n', fp);
    }
    fputs("set noarrow\n", fp);
    for (this_arrow = first_arrow; this_arrow != NULL;
	 this_arrow = this_arrow->next) {
	fprintf(fp, "set arrow %d from ", this_arrow->tag);
	save_position(fp, &this_arrow->start);
	fputs(" to ", fp);
	save_position(fp, &this_arrow->end);
	fprintf(fp, " %s linetype %d linewidth %.3f\n",
		this_arrow->head ? "" : " nohead",
		this_arrow->lp_properties.l_type + 1,
		this_arrow->lp_properties.l_width);
    }
    fputs("set nolinestyle\n", fp);
    for (this_linestyle = first_linestyle; this_linestyle != NULL;
	 this_linestyle = this_linestyle->next) {
	fprintf(fp, "set linestyle %d ", this_linestyle->tag);
	fprintf(fp, "linetype %d linewidth %.3f pointtype %d pointsize %.3f\n",
		this_linestyle->lp_properties.l_type + 1,
		this_linestyle->lp_properties.l_width,
		this_linestyle->lp_properties.p_type + 1,
		this_linestyle->lp_properties.p_size);
    }
    fputs("set nologscale\n", fp);
    if (is_log_x)
	fprintf(fp, "set logscale x %g\n", base_log_x);
    if (is_log_y)
	fprintf(fp, "set logscale y %g\n", base_log_y);
    if (is_log_z)
	fprintf(fp, "set logscale z %g\n", base_log_z);
    if (is_log_x2)
	fprintf(fp, "set logscale x2 %g\n", base_log_x2);
    if (is_log_y2)
	fprintf(fp, "set logscale y2 %g\n", base_log_y2);

    fprintf(fp, "\
set offsets %g, %g, %g, %g\n\
set pointsize %g\n\
set encoding %s\n\
set %spolar\n\
set %sparametric\n\
set view %g, %g, %g, %g\n\
set samples %d, %d\n\
set isosamples %d, %d\n\
set %ssurface\n\
set %scontour",
	    loff, roff, toff, boff,
	    pointsize,
	    encoding_names[encoding],
	    (polar) ? "" : "no",
	    (parametric) ? "" : "no",
	    surface_rot_x, surface_rot_z, surface_scale, surface_zscale,
	    samples_1, samples_2,
	    iso_samples_1, iso_samples_2,
	    (draw_surface) ? "" : "no",
	    (draw_contour) ? "" : "no");

    switch (draw_contour) {
    case CONTOUR_NONE:
	fputc('\n', fp);
	break;
    case CONTOUR_BASE:
	fputs(" base\n", fp);
	break;
    case CONTOUR_SRF:
	fputs(" surface\n", fp);
	break;
    case CONTOUR_BOTH:
	fputs(" both\n", fp);
	break;
    }
    if (label_contours)
	fprintf(fp, "set clabel '%s'\n", contour_format);
    else
	fputs("set noclabel\n", fp);

    fputs("set mapping ", fp);
    switch(mapping3d) {
    case MAP3D_SPHERICAL:
	fputs("spherical\n", fp);
	break;
    case MAP3D_CYLINDRICAL:
	fputs("cylindrical\n", fp);
	break;
    case MAP3D_CARTESIAN:
    default:
	fputs("cartesian\n", fp);
	break;
    }

    if (missing_val != NULL)
	fprintf(fp, "set missing '%s'\n", missing_val);

    save_hidden3doptions(fp);
    fprintf(fp, "set cntrparam order %d\n", contour_order);
    fputs("set cntrparam ", fp);
    switch (contour_kind) {
    case CONTOUR_KIND_LINEAR:
	fputs("linear\n", fp);
	break;
    case CONTOUR_KIND_CUBIC_SPL:
	fputs("cubicspline\n", fp);
	break;
    case CONTOUR_KIND_BSPLINE:
	fputs("bspline\n", fp);
	break;
    }
    fputs("set cntrparam levels ", fp);
    switch (levels_kind) {
    case LEVELS_AUTO:
	fprintf(fp, "auto %d\n", contour_levels);
	break;
    case LEVELS_INCREMENTAL:
	fprintf(fp, "incremental %g,%g,%g\n",
		levels_list[0], levels_list[1],
		levels_list[0] + levels_list[1] * contour_levels);
	break;
    case LEVELS_DISCRETE:
	{
	    int i;
	    fprintf(fp, "discrete %g", levels_list[0]);
	    for (i = 1; i < contour_levels; i++)
		fprintf(fp, ",%g ", levels_list[i]);
	    fputc('\n', fp);
	}
    }
    fprintf(fp, "\
set cntrparam points %d\n\
set size ratio %g %g,%g\n\
set origin %g,%g\n\
set data style ",
	    contour_pts,
	    aspect_ratio, xsize, ysize,
	    xoffset, yoffset);

    switch (data_style) {
    case LINES:
	fputs("lines\n", fp);
	break;
    case POINTSTYLE:
	fputs("points\n", fp);
	break;
    case IMPULSES:
	fputs("impulses\n", fp);
	break;
    case LINESPOINTS:
	fputs("linespoints\n", fp);
	break;
    case DOTS:
	fputs("dots\n", fp);
	break;
    case YERRORBARS:
	fputs("yerrorbars\n", fp);
	break;
    case XERRORBARS:
	fputs("xerrorbars\n", fp);
	break;
    case XYERRORBARS:
	fputs("xyerrorbars\n", fp);
	break;
    case BOXES:
	fputs("boxes\n", fp);
	break;
    case BOXERROR:
	fputs("boxerrorbars\n", fp);
	break;
    case BOXXYERROR:
	fputs("boxxyerrorbars\n", fp);
	break;
    case STEPS:
	fputs("steps\n", fp);
	break;			/* JG */
    case FSTEPS:
	fputs("fsteps\n", fp);
	break;			/* HOE */
    case HISTEPS:
	fputs("histeps\n", fp);
	break;			/* CAC */
    case VECTOR:
	fputs("vector\n", fp);
	break;
    case FINANCEBARS:
	fputs("financebars\n", fp);
	break;
    case CANDLESTICKS:
	fputs("candlesticks\n", fp);
	break;
    }
    fputs("set function style ", fp);
    switch (func_style) {
    case LINES:
	fputs("lines\n", fp);
	break;
    case POINTSTYLE:
	fputs("points\n", fp);
	break;
    case IMPULSES:
	fputs("impulses\n", fp);
	break;
    case LINESPOINTS:
	fputs("linespoints\n", fp);
	break;
    case DOTS:
	fputs("dots\n", fp);
	break;
    case YERRORBARS:
	fputs("yerrorbars\n", fp);
	break;
    case XERRORBARS:
	fputs("xerrorbars\n", fp);
	break;
    case XYERRORBARS:
	fputs("xyerrorbars\n", fp);
	break;
    case BOXXYERROR:
	fputs("boxxyerrorbars\n", fp);
	break;
    case BOXES:
	fputs("boxes\n", fp);
	break;
    case BOXERROR:
	fputs("boxerrorbars\n", fp);
	break;
    case STEPS:
	fputs("steps\n", fp);
	break;			/* JG */
    case FSTEPS:
	fputs("fsteps\n", fp);
	break;			/* HOE */
    case HISTEPS:
	fputs("histeps\n", fp);
	break;			/* CAC */
    case VECTOR:
	fputs("vector\n", fp);
	break;
    case FINANCEBARS:
	fputs("financebars\n", fp);
	break;
    case CANDLESTICKS:
	fputs("candlesticks\n", fp);
	break;
    default:
        /* HBB: default case demanded by gcc, still needed ?? */
	fputs("---error!---\n", fp);
    }

    fprintf(fp, "\
set xzeroaxis lt %d lw %.3f\n\
set x2zeroaxis lt %d lw %.3f\n\
set yzeroaxis lt %d lw %.3f\n\
set y2zeroaxis lt %d lw %.3f\n\
set tics %s\n\
set ticslevel %g\n\
set ticscale %g %g\n",
	    xzeroaxis.l_type + 1, xzeroaxis.l_width,
	    x2zeroaxis.l_type + 1, x2zeroaxis.l_width,
	    yzeroaxis.l_type + 1, yzeroaxis.l_width,
	    y2zeroaxis.l_type + 1, y2zeroaxis.l_width,
	    (tic_in) ? "in" : "out",
	    ticslevel,
	    ticscale, miniticscale);

#define SAVE_XYZLABEL(name,lab) { \
  fprintf(fp, "set %s \"%s\" %f,%f ", \
    name, conv_text(str,lab.text),lab.xoffset,lab.yoffset); \
  fprintf(fp, " \"%s\"\n", conv_text(str, lab.font)); \
}


#define SAVE_MINI(name,m,freq) switch(m&TICS_MASK) { \
 case 0: fprintf(fp, "set no%s\n", name); break; \
 case MINI_AUTO: fprintf(fp, "set %s\n",name); break; \
 case MINI_DEFAULT: fprintf(fp, "set %s default\n",name); break; \
 case MINI_USER: fprintf(fp, "set %s %f\n", name, freq); break; \
}
    SAVE_MINI("mxtics", mxtics, mxtfreq)
	SAVE_MINI("mytics", mytics, mytfreq)
	SAVE_MINI("mx2tics", mx2tics, mx2tfreq)
	SAVE_MINI("my2tics", my2tics, my2tfreq)
	save_tics(fp, xtics, FIRST_X_AXIS, &xticdef, rotate_xtics, "x");
    save_tics(fp, ytics, FIRST_Y_AXIS, &yticdef, rotate_ytics, "y");
    save_tics(fp, ztics, FIRST_Z_AXIS, &zticdef, rotate_ztics, "z");
    save_tics(fp, x2tics, SECOND_X_AXIS, &x2ticdef, rotate_x2tics, "x2");
    save_tics(fp, y2tics, SECOND_Y_AXIS, &y2ticdef, rotate_y2tics, "y2");
    SAVE_XYZLABEL("title", title);

    fprintf(fp, "set %s \"%s\" %s %srotate %f,%f ",
	    "timestamp", conv_text(str, timelabel.text),
	    (timelabel_bottom ? "bottom" : "top"),
	    (timelabel_rotate ? "" : "no"),
	    timelabel.xoffset, timelabel.yoffset);
    fprintf(fp, " \"%s\"\n", conv_text(str, timelabel.font));

    save_range(fp, R_AXIS, rmin, rmax, autoscale_r, "r");
    save_range(fp, T_AXIS, tmin, tmax, autoscale_t, "t");
    save_range(fp, U_AXIS, umin, umax, autoscale_u, "u");
    save_range(fp, V_AXIS, vmin, vmax, autoscale_v, "v");

    SAVE_XYZLABEL("xlabel", xlabel);
    SAVE_XYZLABEL("x2label", x2label);

    if (strlen(timefmt)) {
	fprintf(fp, "set timefmt \"%s\"\n", conv_text(str, timefmt));
    }
    save_range(fp, FIRST_X_AXIS, xmin, xmax, autoscale_x, "x");
    save_range(fp, SECOND_X_AXIS, x2min, x2max, autoscale_x2, "x2");

    SAVE_XYZLABEL("ylabel", ylabel);
    SAVE_XYZLABEL("y2label", y2label);

    save_range(fp, FIRST_Y_AXIS, ymin, ymax, autoscale_y, "y");
    save_range(fp, SECOND_Y_AXIS, y2min, y2max, autoscale_y2, "y2");

    SAVE_XYZLABEL("zlabel", zlabel);
    save_range(fp, FIRST_Z_AXIS, zmin, zmax, autoscale_z, "z");

    fprintf(fp, "set zero %g\n", zero);
    fprintf(fp, "set lmargin %d\nset bmargin %d\nset rmargin %d\nset tmargin %d\n",
	    lmargin, bmargin, rmargin, tmargin);

    fprintf(fp, "set locale \"%s\"\n", cur_locale);
}

static void save_tics(fp, where, axis, tdef, rotate, text)
FILE *fp;
int where;
int axis;
struct ticdef *tdef;
TBOOLEAN rotate;
char *text;
{
    char str[MAX_LINE_LEN + 1];

    if (where == NO_TICS) {
	fprintf(fp, "set no%stics\n", text);
	return;
    }
    fprintf(fp, "set %stics %s %smirror %srotate ", text,
	    (where & TICS_MASK) == TICS_ON_AXIS ? "axis" : "border",
	    (where & TICS_MIRROR) ? "" : "no", rotate ? "" : "no");
    switch (tdef->type) {
    case TIC_COMPUTED:{
	    fputs("autofreq ", fp);
	    break;
	}
    case TIC_MONTH:{
	    fprintf(fp, "\nset %smtics", text);
	    break;
	}
    case TIC_DAY:{
	    fprintf(fp, "\nset %cdtics", axis);
	    break;
	}
    case TIC_SERIES:
	if (datatype[axis] == TIME) {
	    if (tdef->def.series.start != -VERYLARGE) {
		char fd[26];
		gstrftime(fd, 24, timefmt, (double) tdef->def.series.start);
		fprintf(fp, "\"%s\",", conv_text(str, fd));
	    }
	    fprintf(fp, "%g", tdef->def.series.incr);

	    if (tdef->def.series.end != VERYLARGE) {
		char td[26];
		gstrftime(td, 24, timefmt, (double) tdef->def.series.end);
		fprintf(fp, ",\"%s\"", conv_text(str, td));
	    }
	} else {		/* !TIME */

	    if (tdef->def.series.start != -VERYLARGE)
		fprintf(fp, "%g,", tdef->def.series.start);
	    fprintf(fp, "%g", tdef->def.series.incr);
	    if (tdef->def.series.end != VERYLARGE)
		fprintf(fp, ",%g", tdef->def.series.end);
	}

	break;

    case TIC_USER:{
	    register struct ticmark *t;
	    int flag_time;
	    flag_time = (datatype[axis] == TIME);
	    fputs(" (", fp);
	    for (t = tdef->def.user; t != NULL; t = t->next) {
		if (t->label)
		    fprintf(fp, "\"%s\" ", conv_text(str, t->label));
		if (flag_time) {
		    char td[26];
		    gstrftime(td, 24, timefmt, (double) t->position);
		    fprintf(fp, "\"%s\"", conv_text(str, td));
		} else {
		    fprintf(fp, "%g", t->position);
		}
		if (t->next) {
		    fputs(", ", fp);
		}
	    }
	    fputs(")", fp);
	    break;
	}
    }
    putc('\n', fp);
}

static void save_position(fp, pos)
FILE *fp;
struct position *pos;
{
    static char *msg[] =
    {"first ", "second ", "graph ", "screen "};

    assert(first_axes == 0 && second_axes == 1 && graph == 2 && screen == 3);

    fprintf(fp, "%s%g, %s%g, %s%g",
	    pos->scalex == first_axes ? "" : msg[pos->scalex], pos->x,
	    pos->scaley == pos->scalex ? "" : msg[pos->scaley], pos->y,
	    pos->scalez == pos->scaley ? "" : msg[pos->scalez], pos->z);
}

void load_file(fp, name, can_do_args)
FILE *fp;
char *name;
TBOOLEAN can_do_args;
{
    register int len;

    int start, left;
    int more;
    int stop = FALSE;

    lf_push(fp);		/* save state for errors and recursion */
    do_load_arg_substitution = can_do_args;

    if (fp == (FILE *) NULL) {
	/* HBB 980311: alloc() it, to save valuable stack space: */
	char *errbuf = gp_alloc(BUFSIZ, "load_file errorstring");
	(void) sprintf(errbuf, "Cannot open %s file '%s'",
		       can_do_args ? "call" : "load", name);
	os_error(errbuf, c_token);
	free(errbuf);
    } else if (fp == stdin) {
	/* DBT 10-6-98  go interactive if "-" named as load file */
	interactive = TRUE; 
	while(!com_line())
	    ;
    } else {
	/* go into non-interactive mode during load */
	/* will be undone below, or in load_file_error */
	interactive = FALSE;
	inline_num = 0;
	infile_name = name;

	if (can_do_args) {
	    int aix = 0;
	    while (++c_token < num_tokens && aix <= 9) {
		if (isstring(c_token))
		    m_quote_capture(&call_args[aix++], c_token, c_token);
		else
		    m_capture(&call_args[aix++], c_token, c_token);
	    }

/*         A GNUPLOT "call" command can have up to _10_ arguments named "$0"
   to "$9".  After reading the 10th argument (i.e.: "$9") the variable
   'aix' contains the value '10' because of the 'aix++' construction
   in '&call_args[aix++]'.  So I think the following test of 'aix' 
   should be done against '10' instead of '9'.                (JFi) */

/*              if (c_token >= num_tokens && aix > 9) */
	    if (c_token >= num_tokens && aix > 10)
		int_error("too many arguments for CALL <file>", ++c_token);
	}
	while (!stop) {		/* read all commands in file */
	    /* read one command */
	    left = input_line_len;
	    start = 0;
	    more = TRUE;

	    while (more) {
		if (fgets(&(input_line[start]), left, fp) == (char *) NULL) {
		    stop = TRUE;	/* EOF in file */
		    input_line[start] = '\0';
		    more = FALSE;
		} else {
		    inline_num++;
		    len = strlen(input_line) - 1;
		    if (input_line[len] == '\n') {	/* remove any newline */
			input_line[len] = '\0';
			/* Look, len was 1-1 = 0 before, take care here! */
			if (len > 0)
			    --len;
			if (input_line[len] == '\r') {	/* remove any carriage return */
			    input_line[len] = NUL;
			    if (len > 0)
				--len;
			}
		    }
		     else if (len + 2 >= left) {
			extend_input_line();
			left = input_line_len - len - 1;
			start = len + 1;
			continue;	/* don't check for '\' */
		    }
		    if (input_line[len] == '\\') {
			/* line continuation */
			start = len;
			left = input_line_len - start;
		    } else
			more = FALSE;
		}
	    }

	    if (strlen(input_line) > 0) {
		if (can_do_args) {
		    register int il = 0;
		    register char *rl;
		    char *raw_line = gp_strdup(input_line);

		    rl = raw_line;
		    *input_line = '\0';
		    while (*rl) {
			register int aix;
			if (*rl == '$'
			    && ((aix = *(++rl)) != 0)	/* HBB 980308: quiet BCC warning */
			    &&aix >= '0' && aix <= '9') {
			    if (call_args[aix -= '0']) {
				len = strlen(call_args[aix]);
				while (input_line_len - il < len + 1) {
				    extend_input_line();
				}
				strcpy(input_line + il, call_args[aix]);
				il += len;
			    }
			} else {
			    /* substitute for $<n> here */
			    if (il + 1 > input_line_len) {
				extend_input_line();
			    }
			    input_line[il++] = *rl;
			}
			rl++;
		    }
		    if (il + 1 > input_line_len) {
			extend_input_line();
		    }
		    input_line[il] = '\0';
		    free(raw_line);
		}
		screen_ok = FALSE;	/* make sure command line is
					   echoed on error */
		if (do_line())
		    stop = TRUE;
	    }
	}
    }

    /* pop state */
    (void) lf_pop();		/* also closes file fp */
}

/* pop from load_file state stack */
static TBOOLEAN			/* FALSE if stack was empty */
 lf_pop()
{				/* called by load_file and load_file_error */
    LFS *lf;

    if (lf_head == NULL)
	return (FALSE);
    else {
	int argindex;
	lf = lf_head;
	if (lf->fp != (FILE *)NULL && lf->fp != stdin) {
	    /* DBT 10-6-98  do not close stdin in the case
	     * that "-" is named as a load file
	     */
	    (void) fclose(lf->fp);
	}
	for (argindex = 0; argindex < 10; argindex++) {
	    if (call_args[argindex]) {
		free(call_args[argindex]);
	    }
	    call_args[argindex] = lf->call_args[argindex];
	}
	do_load_arg_substitution = lf->do_load_arg_substitution;
	interactive = lf->interactive;
	inline_num = lf->inline_num;
	infile_name = lf->name;
	lf_head = lf->prev;
	free((char *) lf);
	return (TRUE);
    }
}

/* push onto load_file state stack */
/* essentially, we save information needed to undo the load_file changes */
static void lf_push(fp)		/* called by load_file */
FILE *fp;
{
    LFS *lf;
    int argindex;

    lf = (LFS *) gp_alloc(sizeof(LFS), (char *) NULL);
    if (lf == (LFS *) NULL) {
	if (fp != (FILE *) NULL)
	    (void) fclose(fp);	/* it won't be otherwise */
	int_error("not enough memory to load file", c_token);
    }
    lf->fp = fp;		/* save this file pointer */
    lf->name = infile_name;	/* save current name */
    lf->interactive = interactive;	/* save current state */
    lf->inline_num = inline_num;	/* save current line number */
    lf->do_load_arg_substitution = do_load_arg_substitution;
    for (argindex = 0; argindex < 10; argindex++) {
	lf->call_args[argindex] = call_args[argindex];
	call_args[argindex] = NULL;	/* initially no args */
    }
    lf->prev = lf_head;		/* link to stack */
    lf_head = lf;
}

/* used for reread  vsnyder@math.jpl.nasa.gov */
FILE *lf_top()
{
    if (lf_head == (LFS *) NULL)
	return ((FILE *) NULL);
    return (lf_head->fp);
}

/* called from main */
void load_file_error()
{
    /* clean up from error in load_file */
    /* pop off everything on stack */
    while (lf_pop());
}

/* find char c in string str; return p such that str[p]==c;
 * if c not in str then p=strlen(str)
 */
int instring(str, c)
char *str;
int c;
{
    int pos = 0;

    while (str != NULL && *str != NUL && c != *str) {
	str++;
	pos++;
    }
    return (pos);
}

void show_functions()
{
    register struct udft_entry *udf = first_udf;

    fputs("\n\tUser-Defined Functions:\n", stderr);

    while (udf) {
	if (udf->definition)
	    fprintf(stderr, "\t%s\n", udf->definition);
	else
	    fprintf(stderr, "\t%s is undefined\n", udf->udf_name);
	udf = udf->next_udf;
    }
}


void show_at()
{
    (void) putc('\n', stderr);
    disp_at(temp_at(), 0);
}


void disp_at(curr_at, level)
struct at_type *curr_at;
int level;
{
    register int i, j;
    register union argument *arg;

    for (i = 0; i < curr_at->a_count; i++) {
	(void) putc('\t', stderr);
	for (j = 0; j < level; j++)
	    (void) putc(' ', stderr);	/* indent */

	/* print name of instruction */

	fputs(ft[(int) (curr_at->actions[i].index)].f_name, stderr);
	arg = &(curr_at->actions[i].arg);

	/* now print optional argument */

	switch (curr_at->actions[i].index) {
	case PUSH:
	    fprintf(stderr, " %s\n", arg->udv_arg->udv_name);
	    break;
	case PUSHC:
	    (void) putc(' ', stderr);
	    disp_value(stderr, &(arg->v_arg));
	    (void) putc('\n', stderr);
	    break;
	case PUSHD1:
	    fprintf(stderr, " %c dummy\n",
		    arg->udf_arg->udf_name[0]);
	    break;
	case PUSHD2:
	    fprintf(stderr, " %c dummy\n",
		    arg->udf_arg->udf_name[1]);
	    break;
	case CALL:
	    fprintf(stderr, " %s", arg->udf_arg->udf_name);
	    if (level < 6) {
		if (arg->udf_arg->at) {
		    (void) putc('\n', stderr);
		    disp_at(arg->udf_arg->at, level + 2);	/* recurse! */
		} else
		    fputs(" (undefined)\n", stderr);
	    } else
		(void) putc('\n', stderr);
	    break;
	case CALLN:
	    fprintf(stderr, " %s", arg->udf_arg->udf_name);
	    if (level < 6) {
		if (arg->udf_arg->at) {
		    (void) putc('\n', stderr);
		    disp_at(arg->udf_arg->at, level + 2);	/* recurse! */
		} else
		    fputs(" (undefined)\n", stderr);
	    } else
		(void) putc('\n', stderr);
	    break;
	case JUMP:
	case JUMPZ:
	case JUMPNZ:
	case JTERN:
	    fprintf(stderr, " +%d\n", arg->j_arg);
	    break;
	case DOLLARS:
	    fprintf(stderr, " %d\n", arg->v_arg.v.int_val);
	    break;
	default:
	    (void) putc('\n', stderr);
	}
    }
}

/* find max len of keys and count keys with len > 0 */

int find_maxl_keys(plots, count, kcnt)
struct curve_points *plots;
int count, *kcnt;
{
    int mlen, len, curve, cnt;
    register struct curve_points *this_plot;

    mlen = cnt = 0;
    this_plot = plots;
    for (curve = 0; curve < count; this_plot = this_plot->next_cp, curve++)
	if (this_plot->title
	    && ((len = /*assign */ strlen(this_plot->title)) != 0)	/* HBB 980308: quiet BCC warning */
	    ) {
	    cnt++;
	    if (len > mlen)
		mlen = strlen(this_plot->title);
	}
    if (kcnt != NULL)
	*kcnt = cnt;
    return (mlen);
}


/* calculate the number and max-width of the keys for an splot.
 * Note that a blank line is issued after each set of contours
 */
int find_maxl_keys3d(plots, count, kcnt)
struct surface_points *plots;
int count, *kcnt;
{
    int mlen, len, surf, cnt;
    struct surface_points *this_plot;

    mlen = cnt = 0;
    this_plot = plots;
    for (surf = 0; surf < count; this_plot = this_plot->next_sp, surf++) {

	/* we draw a main entry if there is one, and we are
	 * drawing either surface, or unlabelled contours
	 */
	if (this_plot->title && *this_plot->title &&
	    (draw_surface || (draw_contour && !label_contours))) {
	    ++cnt;
	    len = strlen(this_plot->title);
	    if (len > mlen)
		mlen = len;
	}
	if (draw_contour && label_contours && this_plot->contours != NULL) {
	    len = find_maxl_cntr(this_plot->contours, &cnt);
	    if (len > mlen)
		mlen = len;
	}
    }

    if (kcnt != NULL)
	*kcnt = cnt;
    return (mlen);
}

static int find_maxl_cntr(contours, count)
struct gnuplot_contours *contours;
int *count;
{
    register int cnt;
    register int mlen, len;
    register struct gnuplot_contours *cntrs = contours;

    mlen = cnt = 0;
    while (cntrs) {
	if (label_contours && cntrs->isNewLevel) {
	    len = strlen(cntrs->label);
	    if (len)
		cnt++;
	    if (len > mlen)
		mlen = len;
	}
	cntrs = cntrs->next;
    }
    *count += cnt;
    return (mlen);
}

static void save_range(fp, axis, min, max, autosc, text)
FILE *fp;
int axis;
double min, max;
TBOOLEAN autosc;
char *text;
{
    int i;

    i = axis;
    fprintf(fp, "set %srange [ ", text);
    if (autosc & 1) {
	putc('*', fp);
    } else {
	SAVE_NUM_OR_TIME(fp, min, axis);
    }
    fputs(" : ", fp);
    if (autosc & 2) {
	putc('*', fp);
    } else {
	SAVE_NUM_OR_TIME(fp, max, axis);
    }

    fprintf(fp, " ] %sreverse %swriteback",
	    range_flags[axis] & RANGE_REVERSE ? "" : "no",
	    range_flags[axis] & RANGE_WRITEBACK ? "" : "no");

    if (autosc) {
	/* add current (hidden) range as comments */
	fputs("  # (currently [", fp);
	if (autosc & 1) {
	    SAVE_NUM_OR_TIME(fp, min, axis);
	}
	putc(':', fp);
	if (autosc & 2) {
	    SAVE_NUM_OR_TIME(fp, max, axis);
	}
	fputs("] )", fp);
    }
    putc('\n', fp);
}

/* check user defined format strings for valid double conversions */
TBOOLEAN valid_format(format)
const char *format;
{
    for (;;) {
	if (!(format = strchr(format, '%')))	/* look for format spec  */
	    return TRUE;	/* passed Test           */
	do {			/* scan format statement */
	    format++;
	} while (strchr("+-#0123456789.", *format));

	switch (*format) {	/* Now at format modifier */
	case '*':		/* Ignore '*' statements */
	case '%':		/* Char   '%' itself     */
	    format++;
	    continue;
	case 'l':		/* Now we found it !!! */
	    if (!strchr("fFeEgG", format[1]))	/* looking for a valid format */
		return FALSE;
	    format++;
	    break;
	default:
	    return FALSE;
	}
    }
}
