diff src/DLD-FUNCTIONS/gplot.l @ 5102:b04b30d30c66

[project @ 2004-12-28 01:59:05 by jwe]
author jwe
date Tue, 28 Dec 2004 01:59:05 +0000
parents
children 0a048f33a545
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/DLD-FUNCTIONS/gplot.l	Tue Dec 28 01:59:05 2004 +0000
@@ -0,0 +1,1582 @@
+%option prefix="gpt"
+%option noyywrap
+
+%{
+// PKG_ADD: mark_as_rawcommand ("gplot");
+// PKG_ADD: mark_as_rawcommand ("gset");
+// PKG_ADD: mark_as_rawcommand ("gsplot");
+
+// PKG_ADD: mark_as_rawcommand ("replot");
+
+// PKG_ADD: mark_as_command ("gshow");
+// PKG_ADD: mark_as_command ("hold");
+// PKG_ADD: mark_as_command ("set");
+// PKG_ADD: mark_as_command ("show");
+
+// PKG_ADD: __gplot_init__ ();
+
+// PKG_ADD: atexit ("closeplot");
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string>
+#include <fstream>
+#include <iostream>
+
+#ifdef HAVE_UNISTD_H
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <unistd.h>
+#endif
+
+#include "file-ops.h"
+
+#include "defun-dld.h"
+#include "file-io.h"
+#include "gripes.h"
+#include "load-save.h"
+#include "parse.h"
+#include "procstream.h"
+#include "sighandlers.h"
+#include "utils.h"
+#include "variables.h"
+
+enum _toktype
+  {
+    START_PAREN = 1,
+    END_PAREN,
+    START_BRACE,
+    END_BRACE,
+    START_BRACKET,
+    END_BRACKET,
+    COLON,
+    SEMICOLON,
+    COMMA,
+    QUOTE,
+    IDENT,
+    NUMBER,
+    BINOP,
+    UNOP,
+    STRING,
+    OTHER,
+    TITLE,
+    USING,
+    WITH,
+    AXES,
+    CLEAR
+  };
+
+typedef bool (*pred) (const int);
+
+class
+gpt_parse_error
+{
+public:
+  gpt_parse_error (void) : msg () { }
+  gpt_parse_error (std::string errmsg) : msg (errmsg) { }
+
+  std::string msg;
+};
+
+static int is_plot_keyword (const std::string& s);
+
+static int handle_string (char delim);
+
+static inline bool can_be_plotkw (void);
+
+static bool gpt_quote_is_transpose;
+static bool gpt_allow_plotkw;
+static int gpt_parens;
+static int gpt_brackets;
+static int gpt_braces;
+
+static int send_to_plot_stream (const std::string& cmd);
+
+// needed by handle_string
+static char string_buf[256];
+
+%}
+
+D	[0-9]
+S	[ \t]
+IDENT	([_a-zA-Z@][_a-zA-Z0-9]*)
+EXPON	([DdEe][+-]?{D}+)
+NUMBER  (({D}+\.?{D}*{EXPON}?)|(\.{D}+{EXPON}?)|(0[xX][0-9a-fA-F]+))
+NOT	((\~)|(\!))
+/* NOT is not strictly a binary operator, but is close enough for us. */
+BINOP   (({NOT})|(\.?([\*/\\^+-]|\*\*)=?)|([<=~!>&|]=)|([=&|<>]{1,2})|(<<=)|(>>=)|(\.))
+UNOP    ((\+\+)|(\-\-)|(\.'))   /* ' */
+
+%%
+
+"(" {
+    gpt_quote_is_transpose = false;
+    gpt_parens++;
+    return START_PAREN;
+    }
+
+")" {
+    gpt_quote_is_transpose = true;
+    gpt_parens--;
+    return END_PAREN;
+    }
+
+"{" {
+    gpt_quote_is_transpose = false;
+    gpt_braces++;
+    return START_BRACE;
+    }
+
+"}" {
+    gpt_quote_is_transpose = true;
+    gpt_braces--;
+    return END_BRACE;
+    }
+
+"[" {
+    gpt_quote_is_transpose = false;
+    gpt_brackets++;
+    return START_BRACKET;
+    }
+
+"]" {
+    gpt_quote_is_transpose = true;
+    gpt_brackets--;
+    return END_BRACKET;
+    }
+
+":" {
+    gpt_quote_is_transpose = false;
+    return COLON;
+    }
+
+";" {
+    gpt_quote_is_transpose = false;
+    return SEMICOLON;
+    }
+
+"," {
+    gpt_quote_is_transpose = false;
+    return COMMA;
+    }
+
+"'" {
+    if (gpt_quote_is_transpose)
+      {
+        gpt_allow_plotkw = true;
+        return QUOTE;
+      }
+    else
+      {
+        gpt_quote_is_transpose = true;
+        gpt_allow_plotkw = true;
+        return handle_string ('\'');
+      }
+    }
+
+"\"" {
+    return handle_string ('"');
+    }
+
+{IDENT} {
+    int itok;
+    if (can_be_plotkw () && (itok = is_plot_keyword (yytext)))
+      {
+        gpt_quote_is_transpose = false;
+        gpt_allow_plotkw = true;
+        return itok;
+      }
+    else if (std::string (yytext) == "function")
+      {
+        throw gpt_parse_error ("The 'function' keyword is not allowed in plot commands.");
+      }
+    else
+      {
+        gpt_quote_is_transpose = true;
+        gpt_allow_plotkw = true;
+        return IDENT;
+      }
+    }
+
+{D}+/\.[\*/\\^'] | /* ' */
+{NUMBER} {
+    gpt_quote_is_transpose = true;
+    gpt_allow_plotkw = true;
+    return NUMBER;
+    }
+
+{BINOP} {
+    gpt_quote_is_transpose = false;
+    gpt_allow_plotkw = false;
+    return BINOP;
+    }
+
+{UNOP} {
+    gpt_quote_is_transpose = false;
+    gpt_allow_plotkw = true;
+    return UNOP;
+    }
+
+{S} { /* Ignore spaces and tabs outside of character strings. */ }
+
+. {
+    gpt_quote_is_transpose = false;
+    gpt_allow_plotkw = true;
+    warning ("unknown token = \"%s\" in plot command", yytext);
+    return OTHER;
+    }
+
+%%
+
+// If TRUE, a replot command is issued automatically each time a plot
+// changes in some way.
+static bool Vautomatic_replot;
+
+// The name of the shell command to execute to start gnuplot.
+static std::string Vgnuplot_binary;
+
+// TRUE if gnuplot appears to support multiple plot windows with X11.
+static bool Vgnuplot_has_frames;
+
+// The number of lines we've plotted so far.
+static int plot_line_count = 0;
+
+// Is this a parametric plot?  Makes a difference for 3D plotting.
+static bool parametric_plot = false;
+
+// The gnuplot terminal type.
+static std::string gnuplot_terminal_type;
+
+// Should the graph window be cleared before plotting the next line?
+static bool clear_before_plotting = true;
+
+// List of files to delete when we exit or crash.
+//
+// XXX FIXME XXX -- this should really be static, but that causes
+// problems on some systems.
+std::stack <std::string> tmp_files;
+
+// Pipe to gnuplot.
+static oprocstream *plot_stream = 0;
+
+// ID of the plotter process.
+static pid_t plot_stream_pid = 0;
+
+// Gnuplot command strings that we use.
+static std::string Vgnuplot_command_plot;
+static std::string Vgnuplot_command_replot;
+static std::string Vgnuplot_command_splot;
+static std::string Vgnuplot_command_using;
+static std::string Vgnuplot_command_with;
+static std::string Vgnuplot_command_axes;
+static std::string Vgnuplot_command_title;
+static std::string Vgnuplot_command_end;
+
+// (almost) copy-paste code from pt-plot.cc
+
+static std::string
+save_in_tmp_file (const octave_value& t, int ndim = 2, bool parametric = false)
+{
+  std::string name = file_ops::tempnam ("", "oct-");
+
+  if (! name.empty ())
+    {
+      std::ofstream file (name.c_str ());
+
+      if (file)
+	{
+	  switch (ndim)
+	    {
+	    case 2:
+	      save_ascii_data_for_plotting (file, t, name);
+	      break;
+
+	    case 3:
+	      save_three_d (file, t, parametric);
+	      break;
+
+	    default:
+	      gripe_2_or_3_dim_plot ();
+	      break;
+	    }
+	}
+      else
+	{
+	  error ("couldn't open temporary output file `%s'", name.c_str ());
+	  name.resize (0);
+	}
+    }
+
+  return name;
+}
+
+static void
+close_plot_stream (void)
+{
+  octave_child_list::remove (plot_stream_pid);
+
+  if (plot_stream)
+    {
+      send_to_plot_stream ("\nquit\n");
+      delete plot_stream;
+      plot_stream = 0;
+    }
+
+  plot_line_count = 0;
+}
+
+static void
+plot_stream_death_handler (pid_t pid, int)
+{
+  close_plot_stream ();
+
+  warning ("connection to external plotter (pid = %d) lost --", pid);
+  warning ("please try your plot command(s) again");
+}
+
+static void
+open_plot_stream (void)
+{
+  static bool initialized = false;
+
+  if (plot_stream && ! *plot_stream)
+    {
+      delete plot_stream;
+      plot_stream = 0;
+    }
+
+  if (! plot_stream)
+    {
+      initialized = false;
+
+      plot_line_count = 0;
+
+      std::string plot_prog;
+
+      if (Vgnuplot_binary.empty ())
+	plot_prog = "gnuplot";
+      else
+        plot_prog = "\"" + Vgnuplot_binary + "\"";
+
+      // XXX FIXME XXX -- I'm not sure this is the right thing to do,
+      // but without it, C-c at the octave prompt will kill gnuplot...
+
+#if defined (HAVE_POSIX_SIGNALS)
+      sigset_t set, oset;
+      sigemptyset (&set);
+      sigaddset (&set, SIGINT);
+      sigprocmask (SIG_BLOCK, &set, &oset);
+#else
+     volatile octave_interrupt_handler old_interrupt_handler
+	= octave_ignore_interrupts ();
+#endif
+
+      plot_stream = new oprocstream (plot_prog.c_str ());
+
+      if (plot_stream)
+	{
+	  if (! *plot_stream)
+	    {
+	      delete plot_stream;
+	      plot_stream = 0;
+
+	      error ("plot: unable to open pipe to `%s'", plot_prog.c_str ());
+	    }
+	  else
+	    {
+	      plot_stream_pid = plot_stream->pid ();
+    	      octave_child_list::insert (plot_stream_pid,
+					 plot_stream_death_handler);
+	    }
+	}
+      else
+	error ("plot: unable to open pipe to `%s'", plot_prog.c_str ());
+
+#if defined (HAVE_POSIX_SIGNALS)
+      sigprocmask (SIG_SETMASK, &oset, 0);
+#else
+      octave_set_interrupt_handler (old_interrupt_handler);
+#endif
+    }
+
+  if (! error_state && plot_stream && *plot_stream && ! initialized)
+    {
+      initialized = true;
+
+      *plot_stream << "set style data lines\n";
+
+      if (! gnuplot_terminal_type.empty ())
+	*plot_stream << "set term " << gnuplot_terminal_type
+		     << Vgnuplot_command_end << "\n";
+    }
+}
+
+static int
+send_to_plot_stream (const std::string& cmd)
+{
+  if (! (plot_stream && *plot_stream))
+    {
+      open_plot_stream ();
+
+      if (error_state)
+	return -1;
+    }
+
+  int replot_len = Vgnuplot_command_replot.length ();
+  int splot_len = Vgnuplot_command_splot.length ();
+  int plot_len = Vgnuplot_command_plot.length ();
+
+  bool is_replot = (Vgnuplot_command_replot == cmd.substr (0, replot_len));
+  bool is_splot = (Vgnuplot_command_splot == cmd.substr (0, splot_len));
+  bool is_plot = (Vgnuplot_command_plot == cmd.substr (0, plot_len));
+
+  if (plot_line_count == 0 && is_replot)
+    error ("replot: no previous plot");
+  else
+    {
+      *plot_stream << cmd;
+
+      if (! (is_replot || is_splot || is_plot)
+	  && plot_line_count > 0
+	  && Vautomatic_replot)
+	{
+	  *plot_stream << Vgnuplot_command_replot << Vgnuplot_command_end;
+	}
+
+      plot_stream->flush ();
+    }
+
+  return 0;
+}
+
+// Check if the parser state is such that a plot keyword can occur.
+static inline bool
+can_be_plotkw (void)
+{
+    return (gpt_allow_plotkw
+	    && (gpt_braces == 0)
+	    && (gpt_brackets == 0)
+	    && (gpt_parens == 0));
+}
+
+// (Probably not necessesary, but current Matlab style plot functions
+// break without this (they emit too short gnuplot commands))
+static std::string
+plot_style_token (const std::string& s)
+{
+  std::string retval;
+
+  // XXX FIXME XXX -- specify minimum match length for these.
+  static const char *plot_styles[] =
+    {
+      "boxes",
+      "boxerrorbars",
+      "boxxyerrorbars",
+      "candlesticks",
+      "dots",
+      "errorbars",
+      "financebars",
+      "fsteps",
+      "histeps",
+      "impulses",
+      "lines",
+      "linespoints",
+      "points",
+      "steps",
+      "vector",
+      "xerrorbars",
+      "xyerrorbars",
+      "yerrorbars",
+      0,
+    };
+
+  const char * const *tmp = plot_styles;
+  while (*tmp)
+    {
+      if (almost_match (*tmp, s.c_str ()))
+	{
+	  retval = *tmp;
+	  break;
+	}
+
+      tmp++;
+    }
+
+  return retval;
+}
+
+// This is used to handle single-quote delimited strings. Kludge alert.
+static int
+handle_string (char delim)
+{
+  int c;
+  char *pos = string_buf;
+  int escape_pending = 0;
+
+  *pos++ = static_cast<char> (delim);
+  while ((c = yyinput ()) != EOF)
+    {
+      if (c == '\\')
+	{
+	  if (escape_pending)
+	    {
+	      *pos++ = static_cast<char> (c);
+	      escape_pending = 0;
+	    }
+	  else
+	    {
+		*pos++ = static_cast<char> (c);
+		escape_pending = 1;
+	    }
+	  continue;
+	}
+      else if (c == '\n')
+	{
+	  error ("unterminated string constant");
+	  break;
+	}
+      else if (c == delim)
+	{
+	  if (escape_pending)
+	    *pos++ = static_cast<char> (c);
+	  else
+	    {
+	      c = yyinput ();
+	      if (c == delim)
+		{
+		  *pos++ = static_cast<char> (c);
+		  *pos++ = static_cast<char> (c);
+		}
+	      else
+		{
+		  yyunput (c, yytext);
+		  *pos++ = static_cast<char> (delim);
+		  *pos++ = '\0';
+		  yytext = string_buf;
+		  return STRING;
+		}
+	    }
+	}
+      else
+	{
+	  *pos++ = static_cast<char> (c);
+	}
+
+      escape_pending = 0;
+    }
+
+  throw gpt_parse_error ("Unterminated string?");
+
+  return 0;
+}
+
+// Check to see if a character string matches any one of the plot
+// option keywords.  Don't match abbreviations for clear, since that's
+// not a gnuplot keyword (users will probably only expect to be able
+// to abbreviate actual gnuplot keywords).
+
+static int
+is_plot_keyword (const std::string& s)
+{
+  const char *t = s.c_str ();
+  if (almost_match ("title", t, 1))
+    {
+      return TITLE;
+    }
+  else if (almost_match ("using", t, 1))
+    {
+      return USING;
+    }
+  else if (almost_match ("with", t, 1))
+    {
+      return WITH;
+    }
+  else if (almost_match ("axes", t, 2) || almost_match ("axis", t, 2))
+    {
+      return AXES;
+    }
+  else if (strcmp ("clear", t) == 0)
+    {
+      return CLEAR;
+    }
+  else
+    {
+      return 0;
+    }
+}
+
+// Some predicates on tokens
+
+// Return true for ":".
+static inline bool
+colonp (const int tok)
+{
+  return (tok == COLON);
+}
+
+// Return TRUE for "]".
+static inline bool
+endbracketp (const int tok)
+{
+  return (tok == END_BRACKET);
+}
+
+// Return TRUE for plot token, comma or end of input.
+static inline bool
+plottok_or_end_p (const int tok)
+{
+  return (tok == TITLE
+	  || tok == USING
+	  || tok == WITH
+	  || tok == AXES
+	  || tok == CLEAR
+	  || tok == COMMA
+	  || tok == 0);
+}
+
+// Equivalent to (colonp (tok) || plottok_or_end_p (tok)).
+static inline bool
+colon_plottok_or_end_p (const int tok)
+{
+  return (tok == COLON || plottok_or_end_p (tok));
+}
+
+// read until test is true and delimiters are balanced, or end of input.
+// Return the last token in lasttok
+static std::string
+read_until (pred test, int& lasttok) throw (gpt_parse_error)
+{
+  int tok;
+
+  // We have to maintain balanced delimiters per subexpression too.
+  int brackets = 0;
+  int parens = 0;
+  int braces = 0;
+  std::string s;
+
+  tok = gptlex ();
+
+  while (tok && ! (test (tok)
+		   && brackets == 0
+		   && parens == 0
+		   && braces == 0))
+    {
+      switch (tok)
+	{
+	case START_BRACKET:
+	  brackets++;
+	  break;
+
+	case END_BRACKET:
+	  brackets--;
+	  break;
+
+	case START_PAREN:
+	  parens++;
+	  break;
+
+	case END_PAREN:
+	  parens--;
+	  break;
+
+	case START_BRACE:
+	  braces++;
+	  break;
+
+	case END_BRACE:
+	  braces--;
+	  break;
+
+	default:
+	  break;
+        }
+
+      s += std::string (yytext) + " ";
+
+      tok = gptlex ();
+    }
+
+  // Throw error only if we've reached the end token and the test
+  // doesn't accept it.
+
+  if (! test (tok) && ! tok)
+    throw gpt_parse_error ("unexpected end of input");
+
+  lasttok = tok;
+
+  return s;
+}
+
+// Eval the two expressions giving limits of range and print it.
+static std::string
+printrange (std::string starts, std::string ends)
+{
+  octave_value startv, endv;
+  int status;
+  std::string s;
+  OSSTREAM range_buf;
+
+  range_buf << "[";
+
+  if (! starts.empty ())
+    {
+      startv = eval_string (starts, true, status);
+      if (! startv.is_real_scalar ())
+	throw gpt_parse_error ();
+      startv.print_raw (range_buf);
+    }
+
+    range_buf << ":";
+
+    if (! ends.empty ())
+      {
+	endv = eval_string (ends, true, status);
+	if (! endv.is_real_scalar ())
+	  throw gpt_parse_error ();
+        endv.print_raw (range_buf);
+      }
+
+    range_buf << "]";
+    range_buf << OSSTREAM_ENDS;
+
+    s = OSSTREAM_STR (range_buf);
+
+    return s;
+}
+
+// Handle plot parameters.
+
+// Title has one string expression which is evaluated and printed to the
+// gnuplot command string.
+static std::string
+handle_title (int& lasttok)
+{
+  int tok;
+  std::string retstr = Vgnuplot_command_title + " ";
+  std::string title_expr_str;
+
+  title_expr_str += read_until (plottok_or_end_p, tok);
+
+  int status;
+  octave_value tmp_data = eval_string (title_expr_str, true, status);
+
+  if (status != 0 || ! tmp_data.is_defined ())
+    throw gpt_parse_error ();
+
+  OSSTREAM tmp_buf;
+  if (tmp_data.is_string ())
+    {
+      tmp_buf << '"';
+      tmp_data.print_raw (tmp_buf);
+      tmp_buf << '"' << OSSTREAM_ENDS;
+    }
+  else
+    {
+      warning ("line title must be a string");
+      tmp_buf << '"' << "line " << plot_line_count << '"';
+    }
+
+  retstr += OSSTREAM_STR (tmp_buf);
+
+  lasttok = tok;
+
+  return retstr;
+}
+
+// Parse, evaluate and print colon separated expressions in the using
+// plot parameter. The use of trailing format string is not supported.
+static std::string
+handle_using (int& lasttok)
+{
+  int tok;
+  std::string expr_str;
+  std::string retstr = Vgnuplot_command_using + " ";
+  bool out = false;
+
+  octave_value tmp_data;
+  int status;
+  while (! out)
+    {
+      expr_str = read_until (colon_plottok_or_end_p, tok);
+
+      tmp_data = eval_string (expr_str, true, status);
+      if (status != 0 || ! tmp_data.is_real_scalar ())
+	throw gpt_parse_error ();
+
+      OSSTREAM tmp_buf;
+      tmp_data.print_raw (tmp_buf);
+      tmp_buf << OSSTREAM_ENDS;
+      retstr += OSSTREAM_STR (tmp_buf);
+
+      if (tok == COLON)
+	retstr += ":";
+      else
+	out = true;
+    }
+
+  lasttok = tok;
+
+  return retstr;
+}
+
+// Presently just passes the linewidth, pointtype etc. tokens as they are.
+static std::string
+handle_style (int& lasttok)
+{
+  std::string retstr = Vgnuplot_command_with + " ";
+  std::string style;
+
+  lasttok = gptlex ();
+
+  if (lasttok != IDENT)
+    throw gpt_parse_error ("expected plot style token");
+
+  style = std::string (yytext);
+  style = plot_style_token (style);
+
+  if (! style.empty ())
+    retstr += style;
+  else
+    retstr += std::string (yytext);
+
+  // XXX FIXME XXX -- should evaluate the remaining tokens, but this
+  // needs changes in the parser.
+  retstr += " " + read_until (plottok_or_end_p, lasttok);
+
+  return retstr;
+}
+
+// Axes has only one qualifier keyword, which is not evaluated.
+static std::string
+handle_axes (int& lasttok)
+{
+  return Vgnuplot_command_axes + " " + read_until (plottok_or_end_p, lasttok);
+}
+
+// Parse and evaluate parameter string and pass it to gnuplot pipe.
+static int
+makeplot (std::string caller, std::string args) throw (gpt_parse_error)
+{
+  YY_BUFFER_STATE bstate;
+
+  bstate = yy_scan_string (args.c_str ());
+  yy_switch_to_buffer (bstate);
+  std::string outstr;
+  int ndim = 2;
+
+  if (caller == "replot")
+    {
+      ndim = 1;
+      outstr += Vgnuplot_command_replot + " ";
+    }
+  else if (caller == "plot")
+    {
+      ndim = 2;
+      if (clear_before_plotting || plot_line_count == 0)
+	{
+	  plot_line_count = 0;
+	  outstr += Vgnuplot_command_plot + " ";
+	}
+      else
+	outstr += Vgnuplot_command_replot + " ";
+    }
+  else if (caller == "splot")
+    {
+      ndim = 3;
+      if (clear_before_plotting || plot_line_count == 0)
+	{
+	  plot_line_count = 0;
+	  outstr += Vgnuplot_command_splot + " ";
+	}
+      else
+	outstr += Vgnuplot_command_replot + " ";
+    }
+  else
+    throw gpt_parse_error ("unknown plot command");
+
+  gpt_quote_is_transpose = false;
+  gpt_allow_plotkw = false;
+  gpt_parens = 0;
+  gpt_braces = 0;
+  gpt_brackets = 0;
+
+  int tok;
+  tok = gptlex ();
+  if (plottok_or_end_p (tok) && caller != "replot")
+    throw gpt_parse_error ("must have something to plot");
+
+  while (tok)
+    {
+      bool title_set = false;
+      bool using_set = false;
+      bool style_set = false;
+      bool axes_set = false;
+
+      if (tok == START_BRACKET)
+	{
+	  if (caller == "replot")
+	    throw gpt_parse_error ("can't specify new plot ranges with `replot' or while hold is on");
+
+	  std::string xrange_start_str = read_until (colonp, tok);
+	  std::string xrange_end_str = read_until (endbracketp, tok);
+	  outstr += printrange (xrange_start_str, xrange_end_str) + " ";
+	  tok = gptlex ();
+	  if (tok == START_BRACKET)
+	    {
+	      std::string yrange_start_str = read_until (colonp, tok);
+	      std::string yrange_end_str = read_until (endbracketp, tok);
+	      outstr += printrange (yrange_start_str, yrange_end_str) + " ";
+	      tok = gptlex ();
+	      if (tok == START_BRACKET && caller == "gsplot")
+		{
+		  std::string zrange_start_str = read_until (colonp, tok);
+		  std::string zrange_end_str = read_until (endbracketp, tok);
+		  outstr += printrange (zrange_start_str, zrange_end_str) + " ";
+		  tok = gptlex ();
+                }
+            }
+        }
+
+      if (plottok_or_end_p (tok))
+	throw gpt_parse_error ("must have something to plot");
+      else
+	{
+	  std::string file;
+	  plot_line_count++;
+
+	  if (tok == STRING)
+	    {
+	      file = file_ops::tilde_expand (std::string (yytext));
+	      // XXX FIXME XXX -- perhaps should check if the file exists?
+	      outstr += file + " ";
+	      // Discard junk after the string.
+	      read_until (plottok_or_end_p, tok);
+            }
+	  else
+	    {
+	      std::string plot_expr_str;
+	      plot_expr_str += std::string (yytext) + " ";
+	      plot_expr_str += read_until (plottok_or_end_p, tok);
+
+	      int status;
+	      octave_value tmp_data = eval_string (plot_expr_str,
+						   true, status);
+
+	      if (status != 0 || ! tmp_data.is_defined ())
+		throw gpt_parse_error ();
+
+	      OSSTREAM tmp_buf;
+	      tmp_data.print_raw (tmp_buf);
+	      tmp_buf << OSSTREAM_ENDS;
+
+	      if (tmp_data.is_string ())
+		{
+		  file = file_ops::tilde_expand (tmp_data.string_value ());
+		  // XXX FIXME XXX -- perhaps should check if the file exists?
+		  outstr += file + " ";
+                }
+	      else
+		{
+		  switch (ndim)
+		    {
+		    case 2:
+		      file = save_in_tmp_file (tmp_data, ndim);
+		      break;
+
+		    case 3:
+		      file = save_in_tmp_file (tmp_data, ndim,
+					       parametric_plot);
+		      break;
+
+		    default:
+		      gripe_2_or_3_dim_plot ();
+		      break;
+                    }
+
+		  if (file.length () > 0)
+		    {
+		      mark_for_deletion (file);
+		      outstr += "'" + file + "' ";
+		    }
+                }
+            }
+        }
+
+      std::string title_str;
+      std::string using_str;
+      std::string style_str;
+      std::string axes_str;
+
+      bool out = false;
+      while (tok && ! out)
+	{
+	  switch (tok)
+	    {
+	    case COMMA:
+	      out = true;
+	      break;
+
+	    case TITLE:
+	      if (! title_set)
+		title_str += handle_title (tok) + " ";
+	      else
+		throw gpt_parse_error ("only one title option may be specified");
+	      title_set = true;
+	      break;
+
+	    case USING:
+	      if (! using_set)
+		using_str += handle_using (tok) + " ";
+	      else
+		throw gpt_parse_error ("only one using option may be specified");
+	      using_set = true;
+	      break;
+
+	    case WITH:
+	      if (! style_set)
+		style_str += handle_style (tok) + " ";
+	      else
+		throw gpt_parse_error ("only one style option may be specified");
+	      style_set = true;
+	      break;
+
+	    case AXES:
+	      if (! axes_set)
+		axes_str += handle_axes (tok) + " ";
+	      else
+		throw gpt_parse_error ("only one axes option may be specified");
+	      axes_set = true;
+	      break;
+
+	    default:
+	      tok = 0;
+	      break;
+            }
+        }
+
+        if (! title_set)
+	  {
+            OSSTREAM tmp_buf;
+            tmp_buf << Vgnuplot_command_title << " \"line "
+                    << plot_line_count << "\" " << OSSTREAM_ENDS;
+            title_str = OSSTREAM_STR (tmp_buf);
+	    title_set = true;
+        }
+
+	// Plot parameters have to be output in this order.
+	if (using_set)
+	  outstr += using_str;
+
+	if (axes_set)
+	  outstr += axes_str;
+
+	if (title_set)
+	  outstr += title_str;
+
+	if (style_set)
+	  outstr += style_str;
+
+	if (out)
+	  {
+	    // Saw comma on while loop.
+            outstr += ", ";
+            gpt_quote_is_transpose = false;
+            gpt_allow_plotkw = false;
+            gpt_parens = 0;
+            gpt_braces = 0;
+            gpt_brackets = 0;
+            tok = gptlex ();
+	  }
+    }
+
+  outstr += Vgnuplot_command_end;
+
+  // Terrible kludge, but line count is destroyed when plot stream
+  // is opened for the first time.
+  int linesave = plot_line_count;
+  send_to_plot_stream (outstr);
+  plot_line_count = linesave;
+
+  return 1;
+}
+
+static void
+doplot (std::string caller, octave_value_list args)
+{
+  std::string s;
+  int i = 0;
+
+  while (i < args.length ())
+    s += args (i++).string_value () + " ";
+
+  try
+    {
+      makeplot (caller, s);
+    }
+  catch (gpt_parse_error e)
+    {
+      if (e.msg.empty ())
+	error ("could not parse plot command");
+      else
+	error (e.msg.c_str ());
+    }
+}
+
+DEFUN_DLD (gplot, args, ,
+  "Plot with gnuplot.\n")
+{
+  doplot ("plot", args);
+  return octave_value_list ();
+}
+
+DEFUN_DLD (gsplot, args, ,
+  "Plot with gnuplot.\n")
+{
+  doplot ("splot", args);
+  return octave_value_list ();
+}
+
+DEFUN_DLD (replot, args, ,
+  "Plot with gnuplot.\n")
+{
+  doplot ("replot", args);
+  return octave_value_list ();
+}
+
+DEFUN_DLD (clearplot, , ,
+  "-*- texinfo -*-\n\
+@deftypefn {Built-in Function} {} clearplot\n\
+@deftypefnx {Built-in Function} {} clg\n\
+Clear the plot window and any titles or axis labels.  The name\n\
+@code{clg} is aliased to @code{clearplot} for compatibility with\n\
+@sc{Matlab}.\n\
+\n\
+The commands @kbd{gplot clear}, @kbd{gsplot clear}, and @kbd{replot\n\
+clear} are equivalent to @code{clearplot}.  (Previously, commands like\n\
+@kbd{gplot clear} would evaluate @code{clear} as an ordinary expression\n\
+and clear all the visible variables.)\n\
+@end deftypefn")
+{
+  octave_value_list retval;
+
+  send_to_plot_stream ("clear\n");
+
+  // XXX FIXME XXX -- instead of just clearing these things, it would
+  // be nice if we could reset things to a user-specified default
+  // state.
+
+  send_to_plot_stream ("set title\n");
+  send_to_plot_stream ("set xlabel\n");
+  send_to_plot_stream ("set ylabel\n");
+  send_to_plot_stream ("set nogrid\n");
+  send_to_plot_stream ("set nolabel\n");
+
+  // This makes a simple `replot' not work after a `clearplot' command
+  // has been issued.
+
+  plot_line_count = 0;
+
+  return retval;
+}
+
+DEFUN_DLD (closeplot, , ,
+  "-*- texinfo -*-\n\
+@deftypefn {Built-in Function} {} closeplot\n\
+Close stream to the @code{gnuplot} subprocess.  If you are using X11,\n\
+this will close the plot window.\n\
+@end deftypefn")
+{
+  octave_value_list retval;
+  close_plot_stream ();
+  return retval;
+}
+
+DEFUN_DLD (hold, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Built-in Function} {} hold @var{args}\n\
+Tell Octave to `hold' the current data on the plot when executing\n\
+subsequent plotting commands.  This allows you to execute a series of\n\
+plot commands and have all the lines end up on the same figure.  The\n\
+default is for each new plot command to clear the plot device first.\n\
+For example, the command\n\
+\n\
+@example\n\
+hold on\n\
+@end example\n\
+\n\
+@noindent\n\
+turns the hold state on.  An argument of @code{off} turns the hold state\n\
+off, and @code{hold} with no arguments toggles the current hold state.\n\
+@end deftypefn")
+{
+  octave_value_list retval;
+
+  int argc = args.length () + 1;
+
+  string_vector argv = args.make_argv ("hold");
+
+  if (error_state)
+    return retval;
+
+  switch (argc)
+    {
+    case 1:
+      clear_before_plotting = ! clear_before_plotting;
+      break;
+
+    case 2:
+      if (argv[1] == "on")
+	clear_before_plotting = false;
+      else if (argv[1] == "off")
+	clear_before_plotting = true;
+      else
+	print_usage ("hold");
+      break;
+
+    default:
+      print_usage ("hold");
+      break;
+    }
+
+  return retval;
+}
+
+DEFUN_DLD (ishold, , ,
+  "-*- texinfo -*-\n\
+@deftypefn {Built-in Function} {} ishold\n\
+Return 1 if the next line will be added to the current plot, or 0 if\n\
+the plot device will be cleared before drawing the next line.\n\
+@end deftypefn")
+{
+  return octave_value (! clear_before_plotting);
+}
+
+DEFUN_DLD (purge_tmp_files, , ,
+  "-*- texinfo -*-\n\
+@deftypefn {Built-in Function} {} purge_tmp_files\n\
+Delete the temporary files created by the plotting commands.\n\
+\n\
+Octave creates temporary data files for @code{gnuplot} and then sends\n\
+commands to @code{gnuplot} through a pipe.  Octave will delete the\n\
+temporary files on exit, but if you are doing a lot of plotting you may\n\
+want to clean up in the middle of a session.\n\
+\n\
+A future version of Octave will eliminate the need to use temporary\n\
+files to hold the plot data.\n\
+@end deftypefn")
+{
+  octave_value_list retval;
+  cleanup_tmp_files ();
+  return retval;
+}
+
+DEFUN_DLD (graw, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Built-in Function} {} graw (@var{string})\n\
+Send @var{string} directly to gnuplot subprocess.\n\
+@end deftypefn")
+{
+  octave_value_list retval;
+
+  if (args.length () == 1 && args(0).is_string ())
+    {
+      std::string cmd = args(0).string_value ();
+
+      if (! (plot_stream && *plot_stream))
+	open_plot_stream ();
+
+      if (! error_state)
+	{
+	  *plot_stream << cmd;
+
+	  plot_stream->flush ();
+	}
+    }
+  else
+    print_usage ("raw");
+
+  return retval;
+}
+
+DEFUN_DLD (gset, args, ,
+  "-*- texinfo -*-\n\
+@deffn {Command} gset options\n\
+Set plotting options for gnuplot\n\
+@end deffn")
+{
+  octave_value_list retval;
+
+  int argc = args.length () + 1;
+
+  string_vector argv = args.make_argv ("set");
+
+  if (error_state)
+    return retval;
+
+  OSSTREAM plot_buf;
+
+  if (argc > 1)
+    {
+      if (almost_match ("parametric", argv[1], 3))
+	parametric_plot = true;
+      else if (almost_match ("noparametric", argv[1], 5))
+	parametric_plot = false;
+      else if (almost_match ("term", argv[1], 1))
+	{
+	  gnuplot_terminal_type = "";
+	  OSSTREAM buf;
+	  int i;
+	  for (i = 2; i < argc-1; i++)
+	    buf << argv[i] << " ";
+	  if (i < argc)
+	    buf << argv[i];
+	  buf << Vgnuplot_command_end << OSSTREAM_ENDS;
+	  gnuplot_terminal_type = OSSTREAM_STR (buf);
+	  OSSTREAM_FREEZE (buf);
+	}
+    }
+
+  int i;
+  for (i = 0; i < argc-1; i++)
+    plot_buf << argv[i] << " ";
+
+  if (i < argc)
+    plot_buf << argv[i];
+
+  plot_buf << Vgnuplot_command_end << OSSTREAM_ENDS;
+
+  send_to_plot_stream (OSSTREAM_STR (plot_buf));
+
+  OSSTREAM_FREEZE (plot_buf);
+
+  return retval;
+}
+
+DEFUN_DLD (set, args, nargout,
+  "-*- texinfo -*-\n\
+This command is has been replaced by @code{gset}.")
+{
+  warning ("set is obsolete -- use gset instead");
+  return Fgset (args, nargout);
+}
+
+DEFUN_DLD (gshow, args, ,
+  "-*- texinfo -*-\n\
+@deffn {Command} gshow options\n\
+Show plotting options.\n\
+@end deffn")
+{
+  octave_value_list retval;
+
+  int argc = args.length () + 1;
+
+  string_vector argv = args.make_argv ("show");
+
+  if (error_state)
+    return retval;
+
+  OSSTREAM plot_buf;
+
+  int i;
+  for (i = 0; i < argc-1; i++)
+    plot_buf << argv[i] << " ";
+  if (i < argc)
+    plot_buf << argv[i];
+
+  plot_buf << Vgnuplot_command_end << OSSTREAM_ENDS;
+
+  send_to_plot_stream (OSSTREAM_STR (plot_buf));
+
+  OSSTREAM_FREEZE (plot_buf);
+
+  return retval;
+}
+
+DEFUN_DLD (show, args, nargout,
+  "-*- texinfo -*-\n\
+This command is has been replaced by @code{gshow}.")
+{
+  warning ("show is obsolete -- use gshow instead");
+  return Fgshow (args, nargout);
+}
+
+static int
+automatic_replot (void)
+{
+  Vautomatic_replot = check_preference ("automatic_replot");
+
+  return 0;
+}
+
+static int
+set_string_var (std::string& var, const char *nm)
+{
+  int retval = 0;
+
+  std::string s = builtin_string_variable (nm);
+
+  if (s.empty ())
+    {
+      gripe_invalid_value_specified (nm);
+      retval = -1;
+    }
+  else
+    var = s;
+
+  return retval;
+}
+
+static int
+gnuplot_binary (void)
+{
+  return set_string_var (Vgnuplot_binary, "gnuplot_binary");
+}
+
+static int
+gnuplot_command_plot (void)
+{
+  return set_string_var (Vgnuplot_command_plot, "gnuplot_command_plot");
+}
+
+static int
+gnuplot_command_replot (void)
+{
+  return set_string_var (Vgnuplot_command_replot, "gnuplot_command_replot");
+}
+
+static int
+gnuplot_command_splot (void)
+{
+  return set_string_var (Vgnuplot_command_splot, "gnuplot_command_splot");
+}
+
+static int
+gnuplot_command_using (void)
+{
+  return set_string_var (Vgnuplot_command_using, "gnuplot_command_using");
+}
+
+static int
+gnuplot_command_with (void)
+{
+  return set_string_var (Vgnuplot_command_with, "gnuplot_command_with");
+}
+
+static int
+gnuplot_command_axes (void)
+{
+  return set_string_var (Vgnuplot_command_axes, "gnuplot_command_axes");
+}
+
+static int
+gnuplot_command_title (void)
+{
+  return set_string_var (Vgnuplot_command_title, "gnuplot_command_title");
+}
+
+static int
+gnuplot_command_end (void)
+{
+  return set_string_var (Vgnuplot_command_end, "gnuplot_command_end");
+}
+
+static int
+gnuplot_has_frames (void)
+{
+  Vgnuplot_has_frames = check_preference ("gnuplot_has_frames");
+
+  return 0;
+}
+
+DEFUN_DLD (__gplot_init__, , ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} __gplot_init__ ()\n\
+@end deftypefn")
+{
+  octave_value_list retval;
+
+  static bool gplot_initialized = false;
+
+  if (gplot_initialized)
+    return retval;
+
+  gplot_initialized = true;
+
+  DEFVAR (automatic_replot, true, automatic_replot,
+    "-*- texinfo -*-\n\
+@defvr {Built-in Variable} automatic_replot\n\
+You can tell Octave to redisplay the plot each time anything about it\n\
+changes by setting the value of the builtin variable\n\
+@code{automatic_replot} to a nonzero value.  Although it is fairly\n\
+inefficient, especially for large plots, the default value is 1 for\n\
+compatibility with Matlab.\n\
+@end defvr");
+
+  DEFVAR (gnuplot_binary, GNUPLOT_BINARY, gnuplot_binary,
+    "-*- texinfo -*-\n\
+@defvr {Built-in Variable} gnuplot_binary\n\
+The name of the program invoked by the plot command.  The default value\n\
+is @code{\"gnuplot\"}.  @xref{Installation}.\n\
+@end defvr");
+
+  DEFVAR (gnuplot_command_plot, "pl", gnuplot_command_plot,
+    "-*- texinfo -*-\n\
+@defvr {Built-in Variable} gnuplot_command_plot\n\
+@end defvr");
+
+  DEFVAR (gnuplot_command_replot, "rep", gnuplot_command_replot,
+    "-*- texinfo -*-\n\
+@defvr {Built-in Variable} gnuplot_command_replot\n\
+@end defvr");
+
+  DEFVAR (gnuplot_command_splot, "sp", gnuplot_command_splot,
+    "-*- texinfo -*-\n\
+@defvr {Built-in Variable} gnuplot_command_splot\n\
+@end defvr");
+
+  DEFVAR (gnuplot_command_using, "u", gnuplot_command_using,
+    "-*- texinfo -*-\n\
+@defvr {Built-in Variable} gnuplot_command_using\n\
+@end defvr");
+
+  DEFVAR (gnuplot_command_with, "w", gnuplot_command_with,
+    "-*- texinfo -*-\n\
+@defvr {Built-in Variable} gnuplot_command_with\n\
+@end defvr");
+
+  DEFVAR (gnuplot_command_axes, "ax", gnuplot_command_axes,
+    "-*- texinfo -*-\n\
+@defvr {Built-in Variable} gnuplot_command_axes\n\
+@end defvr");
+
+  DEFVAR (gnuplot_command_title, "t", gnuplot_command_title,
+    "-*- texinfo -*-\n\
+@defvr {Built-in Variable} gnuplot_command_title\n\
+@end defvr");
+
+  DEFVAR (gnuplot_command_end, "\n", gnuplot_command_end,
+    "-*- texinfo -*-\n\
+@defvr {Built-in Variable} gnuplot_command_end\n\
+@end defvr");
+
+#if defined (GNUPLOT_HAS_FRAMES)
+  bool with_frames = true;
+#else
+  bool with_frames = false;
+#endif
+
+  DEFVAR (gnuplot_has_frames, with_frames, gnuplot_has_frames,
+    "-*- texinfo -*-\n\
+@defvr {Built-in Variable} gnuplot_has_frames\n\
+If the value of this variable is nonzero, Octave assumes that your copy\n\
+of gnuplot has support for multiple frames that is included in recent\n\
+3.6beta releases.  Its initial value is determined by configure, but it\n\
+can be changed in your startup script or at the command line in case\n\
+configure got it wrong, or if you upgrade your gnuplot installation.\n\
+@end defvr");
+
+  return retval;
+}