Mercurial > hg > octave-nkf
changeset 5790:60659f01c75b
[project @ 2006-05-04 18:33:11 by jwe]
author | jwe |
---|---|
date | Thu, 04 May 2006 18:33:11 +0000 |
parents | 7d77b6839ca7 |
children | 70215aff5ccf |
files | src/ChangeLog src/DLD-FUNCTIONS/__gnuplot_raw__.l src/Makefile.in |
diffstat | 3 files changed, 1771 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,9 @@ +2006-05-04 John W. Eaton <jwe@octave.org> + + * Makefile.in: Undo changes of 2006-03-16. + * DLD-FUNCTINS/__gnuplot_raw__.l: Move here from __gnuplot_raw__.l. + Undo changes of 2006-03-16. + 2006-05-03 David Bateman <dbateman@free.fr> * ov-base-mat.h: Add caching of matrix type, and code to supply
new file mode 100644 --- /dev/null +++ b/src/DLD-FUNCTIONS/__gnuplot_raw__.l @@ -0,0 +1,1762 @@ +/* + +Copyright (C) 2004 John W. Eaton and Teemu Ikonen + +This file is part of Octave. + +Octave 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. + +Octave 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 Octavee; see the file COPYING. If not, write to the Free +Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + +*/ + +%option prefix="gpt" +%option noyywrap + +%{ +// PKG_ADD: mark_as_rawcommand ("__gnuplot_plot__"); +// PKG_ADD: mark_as_rawcommand ("__gnuplot_set__"); +// PKG_ADD: mark_as_rawcommand ("__gnuplot_splot__"); +// PKG_ADD: mark_as_rawcommand ("__gnuplot_replot__"); + +// PKG_ADD: mark_as_command ("__gnuplot_show__"); + +// PKG_ADD: atexit ("closeplot"); + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <map> +#include <string> +#include <fstream> +#include <iostream> +#include <sstream> + +#ifdef HAVE_UNISTD_H +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#include <unistd.h> +#endif + +#include "file-ops.h" +#include "oct-env.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 bool gpt_quote_is_transpose; +static bool gpt_allow_plotkw; +static int gpt_parens; +static int gpt_brackets; +static int gpt_braces; + +static bool can_be_plotkw (void); + +static int is_plot_keyword (const std::string& s); + +static int handle_string (char delim); +static std::string strbuf; + +%} + +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})|(<<=)|(>>=)|(\.)) +/* single quote (') transpose operator is handled separately. */ +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 = 0; + 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 ("function keyword 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; + } + +%% + +// ------------------------------------------------------------ +// Interface to external gnuplot process(es), including gnuplot +// command parser. +// ------------------------------------------------------------ + +// The name of the shell command to execute to start gnuplot. +static std::string Vgnuplot_binary = GNUPLOT_BINARY; + +// Append -title "Figure NN" to the gnuplot command? +static bool Vgnuplot_use_title_option = octave_env::have_x11_display (); + +// Gnuplot command strings that we use. +static std::string Vgnuplot_command_plot = "pl"; +static std::string Vgnuplot_command_replot = "rep"; +static std::string Vgnuplot_command_splot = "sp"; +static std::string Vgnuplot_command_using = "u"; +static std::string Vgnuplot_command_with = "w"; +static std::string Vgnuplot_command_axes = "ax"; +static std::string Vgnuplot_command_title = "t"; +static std::string Vgnuplot_command_end = "\n"; + +// If TRUE, a replot command is issued automatically each time a plot +// changes in some way. +static bool Vautomatic_replot = true; + +// Check if the parser state is such that a plot keyword can occur. + +static bool +can_be_plotkw (void) +{ + return (gpt_allow_plotkw + && (gpt_braces == 0) + && (gpt_brackets == 0) + && (gpt_parens == 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) +{ + if (almost_match ("title", s, 1)) + return TITLE; + else if (almost_match ("using", s, 1)) + return USING; + else if (almost_match ("with", s, 1)) + return WITH; + else if (almost_match ("axes", s, 2) || almost_match ("axis", s, 2)) + return AXES; + else if ("clear" == s) + return CLEAR; + else + return 0; +} + +// This is used to handle character strings. Kludge alert. + +static int +handle_string (char delim) +{ + int c; + bool escape_pending = false; + + strbuf = std::string (1, delim); + + while ((c = yyinput ()) != EOF) + { + if (c == '\\') + { + if (escape_pending) + { + strbuf += static_cast<char> (c); + escape_pending = false; + } + else + { + strbuf += static_cast<char> (c); + escape_pending = true; + } + continue; + } + else if (c == '\n') + { + error ("unterminated string constant"); + break; + } + else if (c == delim) + { + if (escape_pending) + strbuf += static_cast<char> (c); + else + { + c = yyinput (); + + if (c == delim) + { + strbuf += static_cast<char> (c); + strbuf += static_cast<char> (c); + } + else + { + yyunput (c, yytext); + strbuf += static_cast<char> (delim); + return STRING; + } + } + } + else + strbuf += static_cast<char> (c); + + escape_pending = false; + } + + throw gpt_parse_error ("unterminated string"); + + return 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; + + // FIXME -- 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; +} + +// Some predicates on tokens + +// Return true for ":". + +static bool +colonp (const int tok) +{ + return (tok == COLON); +} + +// Return TRUE for "]". + +static bool +endbracketp (const int tok) +{ + return (tok == END_BRACKET); +} + +// Return TRUE for plot token, comma or end of input. + +static 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 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 += (tok == STRING ? strbuf : 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; + std::ostringstream 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 << "]"; + + s = range_buf.str (); + + return s; +} + +// Handle plot parameters. + +// 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 (); + + std::ostringstream tmp_buf; + tmp_data.print_raw (tmp_buf); + retstr += tmp_buf.str (); + + 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); + + // FIXME -- 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); +} + +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 int +get_current_figure (void) +{ + int retval = 1; + + octave_value cf = get_global_value ("__current_figure__", true); + + if (cf.is_defined ()) + retval = cf.int_value (); + else + set_global_value ("__current_figure__", retval); + + return retval; +} + +class +gnuplot +{ +protected: + + gnuplot (void) + : plot_line_count (0), parametric_plot (false), + use_title_option (Vgnuplot_use_title_option), + gnuplot_exe (Vgnuplot_binary), + gnuplot_terminal_type (), plot_stream () { } + +public: + + ~gnuplot (void) { } + + static bool have_instance (void); + + static bool ensure_instance (void); + + static bool ensure_plot_stream (void); + + static void open (void); + + static void close (void); + + static void close_all (void); + + static bool plot_stream_event_handler (pid_t pid, int status); + + static void send (const std::string& cmd) + { + if (ensure_plot_stream ()) + instance->do_send (cmd); + } + + static void send_raw (const std::string& cmd) + { + if (ensure_plot_stream ()) + instance->do_send_raw (cmd); + } + + static void clear (void) + { + if (ensure_plot_stream ()) + instance->do_clear (); + } + + static void set (const string_vector& argv) + { + if (ensure_plot_stream ()) + instance->do_set (argv); + } + + static void show (const string_vector& argv) + { + if (ensure_plot_stream ()) + instance->do_show (argv); + } + + static void set_gnuplot_exe (const std::string& exe) + { + if (ensure_instance ()) + instance->do_set_gnuplot_exe (exe); + } + + static void set_gnuplot_use_title_option (bool opt) + { + if (ensure_instance ()) + instance->do_set_gnuplot_use_title_option (opt); + } + + // FIXME -- should only remove tmp files associated with + // gnuplot? + static void cleanup_tmp_files (void) { ::cleanup_tmp_files (); } + + static void plot (const string_vector& argv) + { + if (ensure_plot_stream ()) + instance->do_plot (argv); + } + +private: + + static gnuplot *instance; + + static std::map<int, gnuplot *> instance_map; + + // The number of lines we've plotted so far. + int plot_line_count; + + // Is this a parametric plot? Makes a difference for 3D plotting. + bool parametric_plot; + + // Should we append '-title "TITLE"' to the gnuplot command? + bool use_title_option; + + // The executable program to run. + std::string gnuplot_exe; + + // The gnuplot terminal type. + std::string gnuplot_terminal_type; + + // Pipe to gnuplot. + oprocstream *plot_stream; + + pid_t pid (void) const; + + static gnuplot *lookup_by_pid (pid_t pid); + + void do_open (void); + + void do_close (void); + + void delete_plot_stream (void); + + void reset_plot_stream (void); + + void do_send (const std::string& cmd); + + void do_send_raw (const std::string& cmd); + + void do_clear (void); + + void do_set (const string_vector& argv); + + void do_show (const string_vector& argv); + + void do_set_gnuplot_exe (const std::string& exe) { gnuplot_exe = exe; } + + void do_set_gnuplot_use_title_option (bool opt) { use_title_option = opt; } + + void do_plot (const string_vector& argv); + + std::string + makeplot (std::string caller, std::string args) throw (gpt_parse_error); + + std::string handle_title (int& lasttok); +}; + +gnuplot *gnuplot::instance = 0; + +std::map<int, gnuplot *> gnuplot::instance_map; + +bool +gnuplot::have_instance (void) +{ + int current_figure = get_current_figure (); + + if (instance_map.find (current_figure) != instance_map.end ()) + { + instance = instance_map[current_figure]; + return true; + } + else + return false; +} + +bool +gnuplot::ensure_instance (void) +{ + if (! have_instance ()) + { + instance = new gnuplot (); + + if (! instance) + { + ::error ("unable to create gnuplot object!"); + + return false; + } + else + instance_map[get_current_figure ()] = instance; + } + + return true; +} + +bool +gnuplot::ensure_plot_stream (void) +{ + if (ensure_instance ()) + { + instance->do_open (); + + if (error_state) + return false; + } + + return true; +} + +void +gnuplot::close (void) +{ + if (have_instance ()) + { + instance->do_close (); + + instance_map.erase (get_current_figure ()); + } +} + +void +gnuplot::close_all (void) +{ + for (std::map<int, gnuplot *>::const_iterator p = instance_map.begin (); + p != instance_map.end (); + p++) + { + gnuplot *elt = p->second; + + elt->do_close (); + } +} + +pid_t +gnuplot::pid (void) const +{ + return plot_stream ? plot_stream->pid () : -1; +} + +gnuplot * +gnuplot::lookup_by_pid (pid_t pid) +{ + gnuplot *retval = 0; + + for (std::map<int, gnuplot *>::const_iterator p = instance_map.begin (); + p != instance_map.end (); + p++) + { + gnuplot *elt = p->second; + + if (elt && elt->pid () == pid) + { + retval = elt; + break; + } + } + + return retval; +} + +void +gnuplot::do_open (void) +{ + static bool initialized = false; + + if (plot_stream && ! *plot_stream) + do_close (); + + if (! plot_stream) + { + initialized = false; + + plot_line_count = 0; + + std::string cmd; + + if (gnuplot_exe.empty ()) + cmd = "gnuplot"; + else + cmd = "\"" + gnuplot_exe + "\""; + + // FIXME -- 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 nset, oset; + sigemptyset (&nset); + sigaddset (&nset, SIGINT); + sigprocmask (SIG_BLOCK, &nset, &oset); +#else + volatile octave_interrupt_handler old_interrupt_handler + = octave_ignore_interrupts (); +#endif + + if (use_title_option) + { + std::ostringstream buf; + + buf << cmd << " -title \"Figure " << get_current_figure () << "\""; + + cmd = buf.str (); + } + + plot_stream = new oprocstream (cmd.c_str ()); + + if (plot_stream && *plot_stream) + octave_child_list::insert (plot_stream->pid (), + plot_stream_event_handler); + else + { + delete_plot_stream (); + + error ("plot: unable to open pipe to `%s'", cmd.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; + + do_send_raw ("set style data lines\n"); + + if (! gnuplot_terminal_type.empty ()) + do_send_raw ("set term " + gnuplot_terminal_type + + Vgnuplot_command_end + "\n"); + } +} + +void +gnuplot::delete_plot_stream (void) +{ + do_send_raw ("\nquit\n"); + + delete plot_stream; + plot_stream = 0; +} + +void +gnuplot::reset_plot_stream (void) +{ + delete_plot_stream (); + + plot_line_count = 0; + parametric_plot = false; +} + +void +gnuplot::do_close (void) +{ + if (plot_stream) + { + octave_child_list::remove (plot_stream->pid ()); + + delete_plot_stream (); + } + + plot_line_count = 0; +} + +bool +gnuplot::plot_stream_event_handler (pid_t pid, int status) +{ + bool retval = false; + + if (pid > 0) + { + if (WIFEXITED (status) || WIFSIGNALLED (status)) + { + gnuplot *plotter = gnuplot::lookup_by_pid (pid); + + // We should only print a warning or request removal of the + // process from the child list if the process died + // unexpectedly. If do_close is responsible for + // gnuplot's death, then plotter will be 0 here and we don't + // need to do anything. + + if (plotter) + { + plotter->reset_plot_stream (); + + warning ("connection to external plotter (pid = %d) lost --", pid); + // Request removal of this PID from the list of child + // processes. + + retval = true; + } + } + } + + return retval; +} + +void +gnuplot::do_send (const std::string& cmd) +{ + int replot_len = Vgnuplot_command_replot.length (); + + bool is_replot = (Vgnuplot_command_replot == cmd.substr (0, replot_len)); + + if (! (plot_line_count == 0 && is_replot)) + do_send_raw (cmd); +} + +void +gnuplot::do_send_raw (const std::string& cmd) +{ + if (plot_stream && *plot_stream) + { + *plot_stream << cmd; + + plot_stream->flush (); + } +} + +void +gnuplot::do_clear (void) +{ + do_send_raw ("clear\n"); + + // FIXME -- instead of just clearing these things, it would + // be nice if we could reset things to a user-specified default + // state. + + do_send_raw ("set title\n"); + do_send_raw ("set xlabel\n"); + do_send_raw ("set ylabel\n"); + do_send_raw ("set nogrid\n"); + do_send_raw ("set nolabel\n"); + + // This makes a simple `replot' not work after a `clearplot' command + // has been issued. + + plot_line_count = 0; +} + +void +gnuplot::do_set (const string_vector& argv) +{ + int argc = argv.length (); + + std::ostringstream 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 = ""; + std::ostringstream buf; + int i; + for (i = 2; i < argc-1; i++) + buf << argv[i] << " "; + if (i < argc) + buf << argv[i]; + buf << Vgnuplot_command_end; + gnuplot_terminal_type = buf.str (); + } + } + + 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; + + do_send_raw (plot_buf.str ()); +} + +void +gnuplot::do_show (const string_vector& argv) +{ + int argc = argv.length (); + + std::ostringstream 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; + + do_send (plot_buf.str ()); +} + +void +gnuplot::do_plot (const string_vector& argv) +{ + std::string s; + + for (int i = 1; i < argv.length (); i++) + s += argv[i] + " "; + + try + { + std::string cmd = makeplot (argv[0], s); + + do_send (cmd); + } + catch (gpt_parse_error& e) + { + if (e.msg.empty ()) + error ("could not parse plot command"); + else + error (e.msg.c_str ()); + } +} + +// Parse and evaluate parameter string and pass it to gnuplot pipe. + +std::string +gnuplot::makeplot (std::string caller, std::string args) + throw (gpt_parse_error) +{ + std::string retval; + + 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; + plot_line_count = 0; + outstr += Vgnuplot_command_plot + " "; + } + else if (caller == "splot") + { + ndim = 3; + plot_line_count = 0; + outstr += Vgnuplot_command_splot + " "; + } + 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)) + return std::string (); + else + { + std::string file; + plot_line_count++; + + std::string plot_expr_str; + plot_expr_str += std::string (yytext) + " "; + plot_expr_str += read_until (plottok_or_end_p, tok); + + int status = 0; + octave_value tmp_data = eval_string (plot_expr_str, + true, status); + + if (status != 0 || ! tmp_data.is_defined ()) + throw gpt_parse_error (); + + std::ostringstream tmp_buf; + tmp_data.print_raw (tmp_buf); + + if (tmp_data.is_string ()) + { + file = file_ops::tilde_expand (tmp_data.string_value ()); + // FIXME -- 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) + { + std::ostringstream tmp_buf; + tmp_buf << Vgnuplot_command_title << " \"line " + << plot_line_count << "\" "; + title_str = tmp_buf.str (); + 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; + + return outstr; +} + +// Title has one string expression which is evaluated and printed to the +// gnuplot command string. + +std::string +gnuplot::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 (); + + std::ostringstream tmp_buf; + if (tmp_data.is_string ()) + { + tmp_buf << '"'; + tmp_data.print_raw (tmp_buf); + tmp_buf << '"'; + } + else + { + warning ("line title must be a string"); + tmp_buf << '"' << "line " << plot_line_count << '"'; + } + + retstr += tmp_buf.str (); + + lasttok = tok; + + return retstr; +} + +// The static instance of this class is here so that +// gnuplot::close_all will be called when the .oct file is unloaded. + +class +gnuplot_X +{ +public: + gnuplot_X (void) { } + ~gnuplot_X (void) { gnuplot::close_all (); } +}; + +static gnuplot_X X; + +static octave_value +set_internal_variable (bool& var, const octave_value_list& args, + const char *nm) +{ + octave_value retval = var; + + int nargin = args.length (); + + if (nargin == 1) + { + bool bval = args(0).bool_value (); + + if (! error_state) + var = bval; + else + error ("%s: expecting arg to be a logical value", nm); + } + else if (nargin > 1) + print_usage ("automatic_replot"); + + return retval; +} + +static octave_value +set_internal_variable (std::string& var, const octave_value_list& args, + const char *nm) +{ + octave_value retval = var; + + int nargin = args.length (); + + if (nargin == 1) + { + std::string sval = args(0).string_value (); + + if (! error_state) + var = sval; + else + error ("%s: expecting arg to be a character string", nm); + } + else if (nargin > 1) + print_usage ("automatic_replot"); + + return retval; +} + +#define SET_INTERNAL_VARIABLE(NM) set_internal_variable (V ## NM, args, #NM) + +// ----------------------- +// User-callable functions +// ----------------------- + +DEFUN_DLD (automatic_replot, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {@var{val} =} automatic_replot ()\n\ +@deftypefnx {Loadable Function} {@var{old_val} =} automatic_replot (@var{new_val})\n\ +Query or set the current automatic replot state. Although it is fairly\n\ +inefficient, especially for large plots, automatic replotting is enabled\n\ +by default for compatibility with Matlab.\n\ +\n\ +When setting the state, @var{new_val}\n\ +@end deftypefn") +{ + return SET_INTERNAL_VARIABLE (automatic_replot); +} + +DEFUN_DLD (gnuplot_binary, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} gnuplot_binary\n\ +The name of the program invoked by the plot command. The default value\n\ +is @code{\"gnuplot\"}. @xref{Installation}.\n\ +@end deftypefn") +{ + octave_value retval = SET_INTERNAL_VARIABLE (gnuplot_binary); + + if (! error_state && args.length () == 1) + gnuplot::set_gnuplot_exe (Vgnuplot_binary); + + return retval; +} + +DEFUN_DLD (gnuplot_command_plot, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {@var{val} = } gnuplot_command_plot ()\n\ +@deftypefnx {Loadable Function} {@var{old_val} = } gnuplot_command_plot (@var{new_val})\n\ +@end deftypefn") +{ + return SET_INTERNAL_VARIABLE (gnuplot_command_plot); +} + +DEFUN_DLD (gnuplot_command_replot, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {@var{val} =} gnuplot_command_replot ()\n\ +@deftypefnx {Loadable Function} {@var{old_val} =} gnuplot_command_replot (@var{new_val})\n\ +@end deftypefn") +{ + return SET_INTERNAL_VARIABLE (gnuplot_command_replot); +} + +DEFUN_DLD (gnuplot_command_splot, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {@var{val} =} gnuplot_command_splot ()\n\ +@deftypefnx {Loadable Function} {@var{old_val} =} gnuplot_command_splot (@var{new_val})\n\ +@end deftypefn") +{ + return SET_INTERNAL_VARIABLE (gnuplot_command_splot); +} + +DEFUN_DLD (gnuplot_command_using, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {@var{val} =} gnuplot_command_using ()\n\ +@deftypefnx {Loadable Function} {@var{old_val} =} gnuplot_command_using (@var{new_val})\n\ +@end deftypefn") +{ + return SET_INTERNAL_VARIABLE (gnuplot_command_using); +} + +DEFUN_DLD (gnuplot_command_with, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {@var{val} =} gnuplot_command_with ()\n\ +@deftypefnx {Loadable Function} {@var{old_val} =} gnuplot_command_with (@var{new_val})\n\ +@end deftypefn") +{ + return SET_INTERNAL_VARIABLE (gnuplot_command_with); +} + +DEFUN_DLD (gnuplot_command_axes, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {@var{val} =} gnuplot_command_axes ()\n\ +@deftypefnx {Loadable Function} {@var{old_val} =} gnuplot_command_axes (@var{new_val})\n\ +@end deftypefn") +{ + return SET_INTERNAL_VARIABLE (gnuplot_command_axes); +} + +DEFUN_DLD (gnuplot_command_title, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {@var{val} =} gnuplot_command_title ()\n\ +@deftypefnx {Loadable Function} {@var{old_val} =} gnuplot_command_title (@var{new_val})\n\ +@end deftypefn") +{ + return SET_INTERNAL_VARIABLE (gnuplot_command_title); +} + +DEFUN_DLD (gnuplot_command_end, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {@var{val} =} gnuplot_command_end ()\n\ +@deftypefnx {Loadable Function} {@var{old_val} =} gnuplot_command_end (@var{new_val})\n\ +@end deftypefn") +{ + return SET_INTERNAL_VARIABLE (gnuplot_command_end); +} + +DEFUN_DLD (gnuplot_use_title_option, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {@var{val} =} gnuplot_use_title_option ()\n\ +@deftypefnx {Loadable Function} {@var{old_val} =} gnuplot_use_title_option (@var{new_val})\n\ +If enabled, append @samp{-title \"Figure NN\"} to the gnuplot command.\n\ +By default, this feature is enabled if the @code{DISPLAY} environment\n\ +variable is set when Octave starts.\n\ +@end deftypefn") +{ + return SET_INTERNAL_VARIABLE (gnuplot_use_title_option); +} + +DEFUN_DLD (clearplot, , , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {} clearplot\n\ +@deftypefnx {Loadable 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\ +@end deftypefn") +{ + gnuplot::clear (); + + octave_value_list args; + + args(0) = "off"; + + feval ("hold", args); + + return octave_value_list (); +} + +DEFUN_DLD (closeplot, , , + "-*- texinfo -*-\n\ +@deftypefn {Loadable 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") +{ + gnuplot::close (); + + return octave_value_list (); +} + +DEFUN_DLD (purge_tmp_files, , , + "-*- texinfo -*-\n\ +@deftypefn {Loadable 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") +{ + gnuplot::cleanup_tmp_files (); + + return octave_value_list (); +} + +DEFUN_DLD (__gnuplot_raw__, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {} __gnuplot_raw__ (@var{string})\n\ +Send @var{string} directly to gnuplot subprocess.\n\ +@end deftypefn") +{ + if (args.length () == 1 && args(0).is_string ()) + { + std::string cmd = args(0).string_value (); + + gnuplot::send_raw (cmd); + } + else + print_usage ("__gnuplot_raw__"); + + return octave_value_list (); +} + +DEFUN_DLD (__gnuplot_set__, args, , + "-*- texinfo -*-\n\ +@deffn {{Loadable Function} __gnuplot_set__ options\n\ +Set plotting options for gnuplot\n\ +@end deffn") +{ + string_vector argv = args.make_argv ("set"); + + if (! error_state) + gnuplot::set (argv); + + return octave_value_list (); +} + +DEFUN_DLD (__gnuplot_show__, args, , + "-*- texinfo -*-\n\ +@deffn {{Loadable Function} __gnuplot_show__ options\n\ +Show plotting options.\n\ +@end deffn") +{ + string_vector argv = args.make_argv ("show"); + + if (! error_state) + gnuplot::show (argv); + + return octave_value_list (); +} + +DEFUN_DLD (__gnuplot_plot__, args, , + "Plot with gnuplot.\n") +{ + string_vector argv = args.make_argv ("plot"); + + if (! error_state) + gnuplot::plot (argv); + + return octave_value_list (); +} + +DEFUN_DLD (__gnuplot_splot__, args, , + "Plot with gnuplot.\n") +{ + string_vector argv = args.make_argv ("splot"); + + if (! error_state) + gnuplot::plot (argv); + + return octave_value_list (); +} + +DEFUN_DLD (__gnuplot_replot__, args, , + "Plot with gnuplot.\n") +{ + string_vector argv = args.make_argv ("replot"); + + if (! error_state) + gnuplot::plot (argv); + + return octave_value_list (); +} + +/* +;;; Local Variables: *** +;;; mode: C++ *** +;;; End: *** +*/
--- a/src/Makefile.in +++ b/src/Makefile.in @@ -52,7 +52,7 @@ pinv.cc qr.cc quad.cc qz.cc rand.cc regexp.cc schur.cc sort.cc \ sparse.cc spchol.cc spdet.cc spkron.cc splu.cc spparms.cc \ spqr.cc sqrtm.cc svd.cc syl.cc time.cc \ - __glpk__.cc __qp__.cc + __gnuplot_raw__.l __glpk__.cc __qp__.cc DLD_SRC := $(addprefix DLD-FUNCTIONS/, $(DLD_XSRC)) @@ -173,7 +173,7 @@ siglist.c sparse-xdiv.cc sparse-xpow.cc strcasecmp.c \ strncase.c strfns.cc symtab.cc syscalls.cc sysdep.cc \ token.cc toplev.cc unwind-prot.cc utils.cc variables.cc \ - xdiv.cc xpow.cc __gnuplot_raw__.l \ + xdiv.cc xpow.cc \ $(OV_SRC) \ $(PT_SRC) @@ -530,7 +530,7 @@ lex.cc : lex.l $(LEX) $(LFLAGS) $< > $(@F) -__gnuplot_raw__.cc : __gnuplot_raw__.l +__gnuplot_raw__.cc : DLD-FUNCTIONS/__gnuplot_raw__.l $(LEX) $(LFLAGS) $< > $(@F) ## We want to force an update of defaults.h and oct-conf.h every