/*	Copyright (C) 1993 Free Software Foundation, Inc.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this software; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */



#include <ctype.h>
#include "global.h"
#include "graph.h"
#include "cmd.h"
#include "line.h"
#include "io-term.h"
#include "info.h"
#include "cell.h"
#include "regions.h"
#include "ref.h"
#include "io-utils.h"

/* Parameters for the next graph that will be drawn. */

/* This determines the type and destination of the ouput.
 * It should be the argument to a  `set term %s'.  For posctscript graphs,
 * we slip in a `set output %s' as well.
 */
static struct line graph_term_cmd;
static struct line graph_output_file;

#ifdef __STDC__
typedef void (*plotter) (void);
#else
typedef void (*plotter) ();
#endif

struct get_symbols_frame;
struct write_data_frame;
#ifdef __STDC__
static void get_symbols_thunk (struct get_symbols_frame * fr,
			       CELL * cell, CELLREF r, CELLREF c);
static void write_data_thunk (struct write_data_frame * fr,
			      CELL * y_cell, CELLREF r, CELLREF c) ;
static void spew_gnuplot (FILE  * fp, struct line * data_files, char * dir,
			  char * dbase); 
static void spew_for_x (void);
static void spew_for_ps (void);
#else
static void get_symbols_thunk ();
static void write_data_thunk ();
static void spew_gnuplot ();
static void spew_for_x ();
static void spew_for_ps ();
#endif

static plotter plot_fn = 0;

char * gnuplot_program = "gnuplot";
char * gnuplot_shell = "/bin/sh";
char * rm_program = "/bin/rm";

char * graph_axis_name [graph_num_axis] =
{
  "x", "y"
};

char *
graph_order_name [graph_num_orders] =
{
  "rows",
  "columns",
};

char *graph_pair_order_name[graph_num_pair_orders] =
{
  "rows of hz pairs",
  "rows of vt pairs",
  "enumerated rows",
  "cols of hz pairs",
  "cols of vt pairs",
  "enumerated cols"
};

int graph_ornt_row_magic [graph_num_pair_orientations] = { 0, 1, 0 };
int graph_ornt_col_magic [graph_num_pair_orientations] = { 1, 0, 0 };


/* `set label %s' for each axis.
 */
static struct line graph_axis_title [graph_num_axis];

/* set logarithmic */
static int graph_logness [graph_num_axis];

/* Arguments to `set range [%s:%s]' */
static struct line graph_rng_lo [graph_num_axis];
static struct line graph_rng_hi [graph_num_axis];

/* The ranges (if any) of the symbolic names
 * for integer coordinates starting at 0.
 * If none, these will have lr == NON_ROW.
 */
static struct rng graph_axis_symbols [graph_num_axis];
static enum graph_ordering graph_axis_ordering [graph_num_axis];

/* Names to print along the axes */
static struct rng graph_axis_labels [graph_num_axis];
static enum graph_pair_ordering graph_axis_label_order [graph_num_axis];

/* Parameters for each dataset. */
#define NUM_DATASETS 10

/* plot .... with %s */
static struct line graph_style [NUM_DATASETS];
static struct line graph_title [NUM_DATASETS];
static struct rng graph_data [NUM_DATASETS];
static enum graph_pair_ordering graph_data_order [graph_num_axis];




#ifdef __STDC__
enum graph_axis
chr_to_axis (int c)
#else
enum graph_axis
chr_to_axis (c)
     int c;
#endif
{
  if (isupper (c))
    c = tolower (c);
  switch (c)
    {
    case 'x':
      return graph_x;
    case 'y':
      return graph_y;
    }
  io_error_msg ("Unkown graph-axis `%c' (try `x' or `y')", c);
  return graph_x;		/* not reached, actualy. */
}


#ifdef __STDC__
enum graph_ordering
chr_to_graph_ordering (int c)
#else
enum graph_ordering
chr_to_graph_ordering (c)
     int c;
#endif
{
  if (isupper (c))
    c = tolower (c);
  switch (c)
    {
    case 'r':
      return graph_rows;
    case 'c':
      return graph_cols;
    }
  io_error_msg ("Unknown cell ordering `%c' (try `r' or `c').", c);
  return graph_rows;		/* not reached */
}

#ifdef __STDC__
enum graph_pair_ordering
chrs_to_graph_pair_ordering (int pair, int dir)
#else
enum graph_pair_ordering
chrs_to_graph_pair_ordering (pair, dir)
     int pair;
     int dir;
#endif
{
  int pair_offset;
  if (isupper (pair))
    pair = tolower (pair);
  if (isupper (dir))
    dir = tolower (dir);

  switch (pair)
    {
    case 'h':
      pair_offset = graph_hz;
      break;
    case 'v':
      pair_offset = graph_vt;
      break;
    case 'i':
      pair_offset = graph_implicit;
      break;
    default:
      pair_offset = graph_implicit;
      io_error_msg
	("graph.c: invalid pair ording `%c' (wanted h, v, or i)", pair);
      /* no deposit... */
    }

  return PAIR_ORDER(chr_to_graph_ordering (dir), pair_offset);
}

#ifdef __STDC__
char *
graph_quoted_str (char *str)
#else
char *
graph_quoted_str (str)
     char *str;
#endif
{
  static struct line buf;
  
  set_line (&buf, "\"");
  while (*str)
    {
      switch (*str)
	{
	case '"':
	  catn_line (&buf, "\\\"", 2);
	  break;
	default:
	  catn_line (&buf, str, 1);
	  break;
	}
      ++str;
    }
  catn_line (&buf, "\"", 1);
  return buf.buf;
}



#ifdef __STDC__
void
graph_x11_mono (void)
#else
void
graph_x11_mono ()
#endif
{
  set_line (&graph_term_cmd, "x11 # b/w");
  set_line (&graph_output_file, "");
  plot_fn = spew_for_x;
}


#ifdef __STDC__
void
graph_x11_color (void)
#else
void
graph_x11_color ()
#endif
{
  set_line (&graph_term_cmd, "X11 # color");
  set_line (&graph_output_file, "");
  plot_fn = spew_for_x;
}


#ifdef __STDC__
void
graph_postscript (char * file, int kind, int spectrum, char * font, int pt_size)
#else
void
graph_postscript (file, kind, spectrum, font, pt_size)
     char * file;
     int kind;
     int spectrum;
     char * font;
     int pt_size;
#endif
{
  if (isupper (kind))
    kind = tolower (kind);
  if (isupper (spectrum))
    spectrum = tolower (spectrum);
  if (!index ("led", kind))
    io_error_msg
      ("Incorrect postscript graph type %c (should be one of l, e, or c)",
       kind);
  if (!index ("mc", spectrum))
    io_error_msg
      ("Incorrect postscript color type %c (should be either m or c)",
       spectrum);
  sprint_line (&graph_term_cmd,
	       "postscript %c %c %s %d  # Postscript",
	       kind, spectrum, graph_quoted_str (font), pt_size);
  set_line (&graph_output_file, file);
  plot_fn = spew_for_ps;
}



#ifdef __STDC__
void
graph_set_axis_title (int axis_c, char * title)
#else
void
graph_set_axis_title (axis_c, title)
     int axis_c;
     char * title;
#endif
{
  enum graph_axis axis = chr_to_axis (axis_c);
  set_line (&graph_axis_title [axis], graph_quoted_str (title));
}

#ifdef __STDC__
void
graph_set_logness (int axis_c, int explicit, int newval)
#else
void
graph_set_logness (axis_c, explicit, newval)
     int axis_c;
     int explicit;
     int newval;
#endif
{
  enum graph_axis axis = chr_to_axis (axis_c);
  static struct line msg_buf;

  if (!explicit)
    newval = !graph_logness [axis];
  else
    newval = (newval > 0);

  graph_logness [axis] = newval;
  sprint_line (&msg_buf, "%slogarithmic %s%s",
	       ((graph_logness [graph_x] || graph_logness [graph_y])
		? "" : "no"),
	       graph_logness [graph_x] ? "x" : "",
	       graph_logness [graph_y] ? "y" : "");
  io_info_msg ("set %s", msg_buf.buf);
}

#ifdef __STDC__
void
graph_check_range (char * val)
#else
void
graph_check_range (val)
     char * val;
#endif
{
  if (says_default (val))
    return;
  while (isspace (*val)) ++val;
  if (*val == '-') ++val;
  while (isspace (*val)) ++val;
  if (!isdigit (*val))
    io_error_msg
      ("Illegal range specifier %s (should be a numer or `def').", val);
  else
    {
      while (*val)
	if (isspace (*val) || isdigit (*val))
	  ++val;
	else
	  break;
      while (isspace (*val)) ++val;
      if (*val == '.') ++val;
      while (*val)
	if (isspace (*val) || isdigit (*val))
	  ++val;
	else
	  io_error_msg
	    ("Illegal range specifier %s (should be a numer or `def').", val);
    }
	    
}


#ifdef __STDC__
void
graph_set_axis_lo (int axis_c, char * val)
#else
void
graph_set_axis_lo (axis_c, val)
     int axis_c;
     char * val;
#endif
{
  enum graph_axis axis = chr_to_axis (axis_c);
  graph_check_range (val);
  set_line (&graph_rng_lo [axis], val);
  graph_axis_symbols [axis].lr = NON_ROW;
}


#ifdef __STDC__
void
graph_set_axis_hi (int axis_c, char * val)
#else
void
graph_set_axis_hi (axis_c, val)
     int axis_c;
     char * val;
#endif
{
  enum graph_axis axis = chr_to_axis (axis_c);
  graph_check_range (val);
  set_line (&graph_rng_hi [axis], val);
  graph_axis_symbols [axis].lr = NON_ROW;
}

#ifdef __STDC__
void
graph_set_axis_symbolic (int axis_c, struct rng * rng, int ordering_c) 
#else
void
graph_set_axis_symbolic (axis_c, rng, ordering_c)
     int axis_c;
     struct rng * rng;
     int ordering_c;
#endif
{
  enum graph_axis axis = chr_to_axis (axis_c);
  enum graph_ordering ordering = chr_to_graph_ordering (ordering_c);
  int top = (rng->hr - rng->lr + 1) * (rng->hc - rng->lc + 1) - 1;
  char buf [64];

  sprintf (buf, "%d.5", top);
  graph_set_axis_lo (axis, "-0.5");
  graph_set_axis_hi (axis, buf);
  graph_axis_symbols [axis] = *rng;
  graph_axis_ordering [axis] = ordering;
}

#ifdef __STDC__
void
graph_set_axis_labels (int axis_c, struct rng * rng, int pair, int dir)
#else
void
graph_set_axis_labels (axis_c, rng, dir, pair)
     int axis_c;
     struct rng * rng;
     int pair;
     int dir;
#endif
{
  enum graph_pair_ordering order = chrs_to_graph_pair_ordering (pair, dir);
  enum graph_axis axis = chr_to_axis (axis_c);
  graph_axis_labels [axis] = *rng;
  graph_axis_label_order [axis] = order;
}

#ifdef __STDC__
void
graph_default_axis_labels (int axis_c)
#else
void
graph_default_axis_labels (axis_c)
     int axis_c;
#endif
{
  enum graph_axis axis = chr_to_axis (axis_c);
  graph_axis_labels [axis].lr = NON_ROW;
}



static char * graph_plot_styles [] =
{
  "lines",
  "points",
  "linespoints",
  "impulses",
  "dots",
   0
};

#ifdef __STDC__
int
graph_check_style (char * name)
#else
int
graph_check_style (name)
     char * name;
#endif
{
  int x =
    words_member (graph_plot_styles, parray_len (graph_plot_styles), name);
  if (x < 0)
    io_error_msg
      ("Invalid line style %s. (Try M-x describe-function set-graph-style).");
  return x;
}


#ifdef __STDC__
void
graph_set_style (int data_set, char * style)
#else
void
graph_set_style (data_set, style)
     int data_set;
     char * style;
#endif
{
  int x = graph_check_style (style);
  if ((data_set < 0) || (data_set >= NUM_DATASETS))
    io_error_msg
      ("set-graph-style -- data-set out of range: %d (should be in [0..%d])",
       data_set, NUM_DATASETS);
  set_line (&graph_style [data_set], graph_plot_styles [x]);
}

#ifdef __STDC__
void
graph_set_data_title (int data_set, char * title)
#else
void
graph_set_data_title (data_set, title)
     int data_set;
     char * title;
#endif
{
  if ((data_set < 0) || (data_set >= NUM_DATASETS))
    io_error_msg
      ("set-graph-title -- data-set out of range: %d (should be in [0..%d])",
       data_set, NUM_DATASETS);
  set_line (&graph_title [data_set], title);
}

#ifdef __STDC__
void
graph_set_data (int data_set, struct rng * rng, int pair, int dir)
#else
void
graph_set_data (data_set, rng, pair, dir)
     int data_set;
     struct rng * rng;
     int pair;
     int dir;
#endif
{
  enum graph_pair_ordering order = chrs_to_graph_pair_ordering (pair, dir);
  if ((data_set < 0) || (data_set >= NUM_DATASETS))
    io_error_msg
      ("set-graph-data -- data-set out of range: %d (should be in [0..%d])",
       data_set, NUM_DATASETS);
  graph_data [data_set] = *rng;
  graph_data_order [data_set] = order;
}




#ifdef __STDC__
void
graph_presets (void)
#else
void
graph_presets ()
#endif
{
  if (using_curses)
    graph_postscript ("#plot.ps", 'd', 'm', "TimesRoman", 12);
  else
    graph_x11_mono ();
  {
    enum graph_axis axis;
    for (axis = graph_x; axis < graph_num_axis; ++axis)
      {
	int axis_c = graph_axis_name [axis][0];
	graph_logness [axis] = 0;
	graph_set_axis_title (axis_c, "");
	graph_set_axis_lo (axis_c, "def");
	graph_set_axis_hi (axis_c, "def");
	graph_set_axis_title (axis_c, "");
	graph_default_axis_labels (axis_c);
      }
  }
}





#ifdef __STDC__
void
graph_clear_datasets (void)
#else
void
graph_clear_datasets ()
#endif
{
  int x;
  for (x = 0; x < NUM_DATASETS; ++x)
    {
      graph_set_style (x, "lines");
      graph_set_data_title (x, "");
      graph_data [x].lr = NON_ROW;
    }
}


#ifdef __STDC__
void
init_graphing (void)
#else
void
init_graphing ()
#endif
{
  gnuplot_program = getenv ("GNUPLOT_PROG");
  if (!gnuplot_program)
    gnuplot_program = "gnuplot";
  gnuplot_shell = getenv ("GNUPLOT_SHELL");
  if (!gnuplot_shell)
    gnuplot_shell = "/bin/sh";
  rm_program = getenv ("RM_PROG");
  if (!rm_program)
    rm_program = "/bin/rm";
  graph_presets ();
  graph_clear_datasets ();
}


#ifdef __STDC__
void
graph_make_info (void)
#else
void
graph_make_info ()
#endif
{
  struct info_buffer * ib = find_or_make_info ("graphing-parameters");
  enum graph_axis axis;

  clear_info (ib);

  {
    print_info
      (ib,
       "");
    print_info
      (ib,
       "Parameter		Value");
    print_info
      (ib,
       "");

    print_info
      (ib,
       "output type		%s",
       graph_term_cmd.buf);
  }

  if (graph_output_file.buf[0])
    print_info
      (ib,
       "output file		%s",
       graph_output_file.buf);
  
  for (axis = graph_x; axis <= graph_y; ++axis)
    print_info
      (ib,
       "%s-axis title		%s",
       graph_axis_name [axis], graph_axis_title[axis].buf);

  {
    print_info
      (ib,
       "logarithmic axes	%s",
       (graph_logness [graph_x]
	? (graph_logness [graph_y] ? "x,y" : "x")
	: (graph_logness [graph_y] ? "y" : "-neither-")));

  }
  for (axis = graph_x; axis <= graph_y; ++axis)
    {
      if (graph_axis_symbols[axis].lr != NON_ROW)
	print_info
	  (ib,
	   	"%s-axis symbols	%s in %s",
	   graph_axis_name [axis],
	   graph_order_name [graph_axis_ordering [axis]],
	   range_name (&graph_axis_symbols [axis]));
      else
	print_info
	  (ib,
		"%s-axis range		[%s..%s]",
	   graph_axis_name [axis],
	   graph_rng_lo [axis].buf, graph_rng_hi [axis].buf,
	   graph_rng_hi [axis].buf, graph_rng_hi [axis].buf);
    }

  for (axis = graph_x; axis <= graph_y; ++axis)
    {
      if (graph_axis_labels[axis].lr != NON_ROW)
	print_info
	  (ib,
	   	"%s-axis labels		%s in %s",
	   graph_axis_name [axis],
	   graph_pair_order_name [graph_axis_label_order [axis]],
	   range_name (&graph_axis_labels [axis]));
    }

  {
    int x;
    for (x = 0; x < NUM_DATASETS; ++x)
      if (graph_data [x].lr != NON_ROW)
      {
	print_info (ib, "");
	print_info (ib,"Data Set %d%s%s",
		    x,
		    graph_title[x].buf[0] ? " entitled " : "",
		    graph_title[x].buf);
	print_info (ib,"  data for this set: %s in %s",
		    graph_pair_order_name [graph_data_order [x]],
		    range_name (&graph_data[x]));
	print_info (ib,"  style for this set: %s",
		    graph_style[x].buf);
	print_info (ib,"");
      }
  }
}



#ifdef __STDC__
static FILE *
mk_tmp_file (struct line * line, char * dir, char * base)
#else
static FILE *
mk_tmp_file (line, dir, base)
     struct line * line;
     char * dir;
     char * base;
#endif
{
  set_line (line, tmpnam (0));
  return fopen (line->buf, "w");
}


#ifdef __STDC__
void
for_pairs_in (struct rng * rng, enum graph_pair_ordering order, fpi_thunk thunk, void * frame)
#else
void
for_pairs_in (rng, order, thunk, frame)
     struct rng * rng;
     enum graph_pair_ordering order;
     fpi_thunk thunk;
     void * frame;
#endif
{
  CELLREF r;
  CELLREF c;
  enum graph_pair_orientation ornt = order % graph_num_pair_orientations;
  enum graph_ordering dir = ORDER_OF_PAIRS(order);
  int r_inc = 1 + graph_ornt_row_magic [ornt];
  int c_inc = 1 + graph_ornt_col_magic [ornt];
  if (dir == graph_rows)
    {
      r = rng->lr;
      while (1)
	{
	  c = rng->lc;
	  while (1)
	    {
	      CELLREF y_r = r + graph_ornt_row_magic [ornt];
	      CELLREF y_c = c + graph_ornt_col_magic [ornt];
	      CELL * cell = find_cell (y_r, y_c);
	      thunk (frame, cell, y_r, y_c);
	      if ((rng->hc - c) < c_inc)
		break;
	      c += c_inc;
	    }
	  if ((rng->hr - r) < r_inc)
	    break;
	  r += r_inc;
	}
    }
  else
    {
      c = rng->lc;
      while (1)
	{
	  r = rng->lr;
	  while (1)
	    {
	      CELLREF y_r = r + graph_ornt_row_magic [ornt];
	      CELLREF y_c = c + graph_ornt_col_magic [ornt];
	      CELL * cell = find_cell (y_r, y_c);
	      thunk (frame, cell, y_r, y_c);
	      if ((rng->hr - r) < r_inc)
		break;
	      r += r_inc;
	    }
	  if ((rng->hc - c) < c_inc)
	    break;
	  c += c_inc;
	}
    }
}

struct write_tics_frame
{
  FILE * fp;
  enum graph_axis axis;
  enum graph_pair_orientation ornt;
  int tic_cnt;
};

#ifdef __STDC__
static void
write_tics_thunk (struct write_tics_frame * fr, CELL * cp, CELLREF r, CELLREF c)
#else
static void
write_tics_thunk (fr, cp, r, c)
     struct write_tics_frame * fr;
     CELL * cp;
     CELLREF r;
     CELLREF c;
#endif
{
  char * str = graph_quoted_str (print_cell (cp));
  if (fr->tic_cnt)
    fputs (", ", fr->fp);
  fprintf (fr->fp, "%s ", str);
  if (fr->ornt == graph_implicit)
    fprintf (fr->fp, "%d", fr->tic_cnt);
  else
    {
      CELLREF x_r = r - graph_ornt_row_magic [fr->ornt];
      CELLREF x_c = c - graph_ornt_col_magic [fr->ornt];
      
      fprintf (fr->fp, "%s", print_cell (find_cell (x_r, x_c)));
    }
  ++fr->tic_cnt;
}


#ifdef __STDC__
static void
write_tics_command (FILE * fp, enum graph_axis axis, struct rng * rng, enum graph_pair_ordering order)
#else
static void
write_tics_command (fp, axis, rng, order)
     FILE * fp;
     enum graph_axis axis;
     struct rng * rng;
     enum graph_pair_ordering order;
#endif
{
  struct write_tics_frame fr;
  fr.fp = fp;
  fr.axis = axis;
  fr.tic_cnt = 0;
  fr.ornt = order % graph_num_pair_orientations;
  fprintf (fp, "set %stics (", graph_axis_name[axis]);
  for_pairs_in (rng, order, (fpi_thunk)write_tics_thunk, &fr);
  fputs (")\n", fp);
}

struct get_symbols_frame
{
  int symbols;
  char ** names;
};

#ifdef __STDC__
static void
get_symbols_thunk (struct get_symbols_frame * fr,
		   CELL * cell, CELLREF r, CELLREF c)
#else
static void
get_symbols_thunk (fr, cell, r, c)
     struct get_symbols_frame * fr;
     CELL * cell;
     CELLREF r;
     CELLREF c;
#endif
{
  fr->names = (char **)ck_realloc (fr->names,
				   (fr->symbols + 1) * sizeof (char *));
  fr->names [fr->symbols] = ck_savestr (print_cell (cell));
  ++fr->symbols;
}


struct write_data_frame
{
  FILE * fp;
  enum graph_pair_ordering ornt;
  int data_cnt;
  struct get_symbols_frame gsf;
};

#ifdef __STDC__
static void
write_data_thunk (struct write_data_frame * fr,
		  CELL * y_cell, CELLREF r, CELLREF c) 
#else
static void
write_data_thunk (fr, y_cell, r, c)
     struct write_data_frame * fr;
     CELL * y_cell;
     CELLREF r;
     CELLREF c;
#endif
{
  if (!y_cell || !GET_TYP(y_cell))
    return;
  if (fr->ornt == graph_implicit)
    fprintf (fr->fp, "%d ", fr->data_cnt);
  else
    {
      CELLREF x_r = r - graph_ornt_row_magic [fr->ornt];
      CELLREF x_c = c - graph_ornt_col_magic [fr->ornt];
      CELL * x_cell = find_cell (x_r, x_c);
      if (x_cell && GET_TYP(x_cell))
	{
	  if (graph_axis_symbols [graph_x].lr == NON_ROW)
	    fprintf (fr->fp, "%s ", print_cell (x_cell));
	  else
	    {
	      char * key = print_cell (x_cell);
	      int x;
	      for (x = 0; x < fr->gsf.symbols; ++x)
		if (stricmp (key, fr->gsf.names[x]))
		  {
		    fprintf (fr->fp, "%d ", x);
		    break;
		  }
	    }
	}
    }
  fprintf (fr->fp, " %s\n", print_cell (y_cell));
  ++fr->data_cnt;
}


#ifdef __STDC__
static void
spew_gnuplot (FILE  * fp, struct line * data_files, char * dir, char * dbase)
#else
static void
spew_gnuplot (fp, data_files, dir, dbase)
     FILE  * fp;
     struct line * data_files;
     char * dir;
     char * dbase;
#endif
{
  fprintf (fp, "set terminal %s\n", graph_term_cmd.buf);
  fprintf (fp, "set output %s\n",
	   (graph_output_file.buf[0]
	    ? graph_quoted_str (graph_output_file.buf)
	    : ""));

  {
    enum graph_axis axis;
    for (axis = graph_x; axis <= graph_y; ++axis)
      {
	fprintf (fp, "set %slabel %s\n", graph_axis_name[axis],
		 (graph_axis_title[axis].buf[0]
		  ? graph_axis_title[axis].buf
		  : ""));

	fprintf (fp, "set %srange [%s:%s]\n",
		 graph_axis_name [axis],
		 (says_default (graph_rng_lo [axis].buf)
		  ? ""
		  : graph_rng_lo[axis].buf),
		 (says_default (graph_rng_hi [axis].buf)
		  ? ""
		  : graph_rng_hi[axis].buf));
	if (   (graph_axis_symbols [axis].lr != NON_ROW)
	    && (graph_axis_labels [axis].lr == NON_ROW))
	  write_tics_command (fp, axis, &graph_axis_symbols [axis],
			      PAIR_ORDER(graph_axis_ordering[axis],
						   graph_implicit));
	else if (graph_axis_labels [axis].lr != NON_ROW)
	  write_tics_command (fp, axis,
			      &graph_axis_labels [axis],
			      graph_axis_label_order [axis]);
	else
	  fprintf (fp, "set %stics\n", graph_axis_name [axis]);
      }
    if (!(graph_logness[graph_x] && graph_logness[graph_y]))
      fprintf (fp, "set nolog %s%s\n",
	       graph_logness[graph_x] ? "" : "x",
	       graph_logness[graph_y] ? "" : "y");

    if (graph_logness[graph_x] || graph_logness[graph_y])
      fprintf (fp, "set log %s%s\n",
	       graph_logness[graph_x] ? "x" : "",
	       graph_logness[graph_y] ? "y" : "");
  }
  {
    int need_comma = 0;
    int x;
    fprintf (fp, "plot ");
    for (x = 0; x < NUM_DATASETS; ++x)
      {
	init_line (&data_files [x]);
	if (graph_data [x].lr != NON_ROW)
	  {
	    FILE * df = mk_tmp_file (&data_files [x], dir, dbase);
	    struct write_data_frame wdf;
	    if (!df)
	      {
		/* a small core leak here... */
		io_error_msg ("Error opening temp file `%s' for graph data.",
			      data_files [x].buf);
	      }
	    wdf.fp = df;
	    wdf.ornt = graph_data_order [x] % graph_num_pair_orientations;
	    wdf.data_cnt = 0;
	    wdf.gsf.symbols = 0;
	    wdf.gsf.names = 0;
	    if (graph_axis_symbols [graph_x].lr != NON_ROW)
	      for_pairs_in (&graph_axis_symbols [graph_x],
			    PAIR_ORDER (graph_axis_ordering [graph_x],
					graph_implicit),
			    (fpi_thunk)get_symbols_thunk,
			    &wdf.gsf);
	    for_pairs_in (&graph_data [x], graph_data_order [x],
			  (fpi_thunk)write_data_thunk, &wdf);
	    if (graph_axis_symbols [graph_x].lr != NON_ROW)
	      {
		int x;
		for (x = 0; x < wdf.gsf.symbols; ++x)
		  free (wdf.gsf.names[x]);
		ck_free (wdf.gsf.names);
	      }
	    fprintf (fp, "%s %s title %s with %s",
		     need_comma ? "," : "",
		     graph_quoted_str (data_files [x].buf),
		     graph_title [x].buf,
		     graph_style [x].buf);
	    need_comma = 1;
	    fclose (df);
	  }
      }
    fprintf (fp, "\n");
  }
}


#ifdef __STDC__
static void
graph_spew_with_parameters (struct line * shell_script, struct line *
			    gp_script, char * last_cmd, char * dir, char *
			    dbase, char * gbase, char * sbase, int run_gnuplot)
#else
static void
graph_spew_with_parameters (shell_script, gp_script, last_cmd, dir, dbase,
			    gbase, sbase, run_gnuplot)
     struct line * shell_script;
     struct line * gp_script;
     char * last_cmd;
     char * dir;
     char * dbase;
     char * gbase;
     char * sbase;
     int run_gnuplot;
#endif
{
  struct line data_files [NUM_DATASETS];
  FILE * fp;
  fp = mk_tmp_file (gp_script, dir, gbase);
  if (!fp)
    {
      /* coreleak filename */
      io_error_msg ("Error opening tmp file `%s' for plot script.",
		    gp_script->buf);
    }
  spew_gnuplot (fp, data_files, dir, dbase);
  if (last_cmd)
    fputs (last_cmd, fp);
  if (sbase)
    {
      FILE * shfp;
      init_line (shell_script);
      shfp = mk_tmp_file (shell_script, dir, sbase);
      fprintf (shfp, "#!%s\n", gnuplot_shell);
      fputs ("\n", shfp);
      if (run_gnuplot)
	fprintf (shfp, "%s %s\n", gnuplot_program, gp_script->buf);
      fprintf (shfp, "%s %s %s  ",
	       rm_program, gp_script->buf, shell_script->buf);
      {
	int x;
	for (x = 0; x < NUM_DATASETS; ++x)
	  if (data_files[x].buf)
	    fprintf (shfp, "%s ", data_files [x].buf);
      }
      fputs ("\n", shfp);
      fclose (shfp);
    }
  fclose (fp);
}


static FILE * pipe_to_gnuplot = 0;
static char * cleanup_script = 0;
static char * gnuplot_script = 0;

#ifdef __STDC__
static void death_to_gnuplot (void);
#else
static void death_to_gnuplot ();
#endif

#ifdef __STDC__
static void
gnuplot_exception (int fd)
#else
static void
gnuplot_exception (fd)
     int fd;
#endif
{
  death_to_gnuplot ();
}

#ifdef __STDC__
static void
gnuplot_writable (int fd)
#else
static void
gnuplot_writable (fd)
     int fd;
#endif
{
  FD_CLR (fd, &write_fd_set);
  file_write_hooks [fd].hook_fn = 0;
  fprintf (pipe_to_gnuplot, "\n\nload %s\n", graph_quoted_str (gnuplot_script));
  fflush (pipe_to_gnuplot);
}

#ifdef __STDC__
static void
ensure_gnuplot_pipe (void)
#else
static void
ensure_gnuplot_pipe ()
#endif
{
  if (!pipe_to_gnuplot)
    {
      pipe_to_gnuplot = popen (gnuplot_program, "w");
      if (!pipe_to_gnuplot)
	io_error_msg ("Can't popen gnuplot.");
    }
  if (gnuplot_script)
    {
      file_write_hooks [fileno (pipe_to_gnuplot)].hook_fn = gnuplot_writable;
      file_exception_hooks [fileno (pipe_to_gnuplot)].hook_fn = gnuplot_exception;
      FD_SET (fileno (pipe_to_gnuplot), &write_fd_set);
      FD_SET (fileno (pipe_to_gnuplot), &exception_fd_set);
    }
}

#ifdef __STDC__
static void
death_to_gnuplot (void)
#else
static void
death_to_gnuplot ()
#endif
{
  if (pipe_to_gnuplot)
    {
      int fd = fileno (pipe_to_gnuplot);
      file_write_hooks [fd].hook_fn = 0;
      file_exception_hooks [fd].hook_fn = 0;
      FD_CLR (fd, &write_fd_set);
      FD_CLR (fd, &exception_fd_set);
      pclose (pipe_to_gnuplot);
    }
  pipe_to_gnuplot = 0;
  if (cleanup_script)
    system (cleanup_script);
  if (cleanup_script)
    ck_free (cleanup_script);
  if (gnuplot_script)
    ck_free (gnuplot_script);
}





#ifdef __STDC__
static void
spew_for_x (void)
#else
static void
spew_for_x ()
#endif
{
  struct line shell_script;
  struct line gp_script;
  init_line (&shell_script);
  init_line (&gp_script);
  graph_spew_with_parameters (&shell_script, &gp_script, "pause -1\n", 0,
			      "#data", "#plot", "#sh", 0);
  splicen_line (&shell_script, " ", 1, 0);
  splicen_line (&shell_script, gnuplot_shell, strlen (gnuplot_shell), 0);
  if (cleanup_script)
    {
      system (cleanup_script);
      free (cleanup_script);
    }
  cleanup_script = shell_script.buf;
  if (gnuplot_script)
    free (gnuplot_script);
  gnuplot_script = gp_script.buf;
  ensure_gnuplot_pipe ();
}

#ifdef __STDC__
static void
spew_for_ps (void)
#else
static void
spew_for_ps ()
#endif
{
  struct line shell_script;
  struct line gp_script;
  init_line (&shell_script);
  init_line (&gp_script);
  graph_spew_with_parameters (&shell_script, &gp_script, 0, "", "#data",
			      "#plot", "#sh", 1); 
  splicen_line (&shell_script, " ", 1, 0);
  splicen_line (&shell_script, gnuplot_shell, strlen (gnuplot_shell), 0);
  system (shell_script.buf);
  free_line (&shell_script);
  free_line (&gp_script);
}

#ifdef __STDC__
void
graph_plot (void)
#else
void
graph_plot ()
#endif
{
  plot_fn ();
}

