--- /dev/null
+#ifndef lint
+static char *RCSid() { return RCSid("$Id: term.c,v 1.151.2.6 2008/12/12 06:57:50 sfeam Exp $"); }
+#endif
+
+/* GNUPLOT - term.c */
+
+/*[
+ * Copyright 1986 - 1993, 1998, 2004 Thomas Williams, Colin Kelley
+ *
+ * Permission to use, copy, and distribute this software and its
+ * documentation for any purpose with or without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation.
+ *
+ * Permission to modify the software is granted, but not the right to
+ * distribute the complete modified source code. Modifications are to
+ * be distributed as patches to the released version. Permission to
+ * distribute binaries produced by compiling modified sources is granted,
+ * provided you
+ * 1. distribute the corresponding source modifications from the
+ * released version in the form of a patch file along with the binaries,
+ * 2. add special version identification to distinguish your version
+ * in addition to the base release version number,
+ * 3. provide your name and address as the primary contact for the
+ * support of your modified version, and
+ * 4. retain our contact information in regard to use of the base
+ * software.
+ * Permission to distribute the released version of the source code along
+ * with corresponding source modifications in the form of a patch file is
+ * granted with same provisions 2 through 4 for binary distributions.
+ *
+ * This software is provided "as is" without express or implied warranty
+ * to the extent permitted by applicable law.
+]*/
+
+
+ /* This module is responsible for looking after the terminal
+ * drivers at the lowest level. Only this module (should)
+ * know about all the various rules about interpreting
+ * the terminal capabilities exported by the terminal
+ * drivers in the table.
+ *
+ * Note that, as far as this module is concerned, a
+ * terminal session lasts only until _either_ terminal
+ * or output file changes. Before either is changed,
+ * the terminal is shut down.
+ *
+ * Entry points : (see also term/README)
+ *
+ * term_set_output() : called when set output invoked
+ *
+ * term_initialise() : optional. Prepare the terminal for first
+ * use. It protects itself against subsequent calls.
+ *
+ * term_start_plot() : called at start of graph output. Calls term_init
+ * if necessary
+ *
+ * term_apply_lp_properties() : apply linewidth settings
+ *
+ * term_end_plot() : called at the end of a plot
+ *
+ * term_reset() : called during int_error handling, to shut
+ * terminal down cleanly
+ *
+ * term_start_multiplot() : called by set multiplot
+ *
+ * term_end_multiplot() : called by set nomultiplot
+ *
+ * term_check_multiplot_okay() : called just before an interactive
+ * prompt is issued while in multiplot mode,
+ * to allow terminal to suspend if necessary,
+ * Raises an error if interactive multiplot
+ * is not supported.
+ */
+
+#include "term_api.h"
+
+#include "alloc.h"
+#include "axis.h"
+#include "bitmap.h"
+#include "command.h"
+#include "driver.h"
+#include "graphics.h"
+#include "help.h"
+#include "plot.h"
+#include "tables.h"
+#include "term.h"
+#include "util.h"
+#include "version.h"
+#include "misc.h"
+#include "getcolor.h"
+
+#ifdef USE_MOUSE
+#include "mouse.h"
+static int save_mouse_state = 1;
+#endif
+
+#ifdef _Windows
+FILE *open_printer __PROTO((void)); /* in wprinter.c */
+void close_printer __PROTO((FILE * outfile));
+# ifdef __MSC__
+# include <malloc.h>
+# else
+# include <alloc.h>
+# endif /* MSC */
+#endif /* _Windows */
+
+static int termcomp __PROTO((const generic * a, const generic * b));
+
+/* Externally visible variables */
+/* the central instance: the current terminal's interface structure */
+struct termentry *term = NULL; /* unknown */
+
+/* ... and its options string */
+char term_options[MAX_LINE_LEN+1] = "";
+
+/* the 'output' file name and handle */
+char *outstr = NULL; /* means "STDOUT" */
+FILE *gpoutfile;
+
+/* Output file where the PostScript output goes to. See term_api.h for more
+ details.
+*/
+FILE *gppsfile = 0;
+
+/* true if terminal has been initialized */
+TBOOLEAN term_initialised;
+
+/* true if in multiplot mode */
+TBOOLEAN multiplot = FALSE;
+
+/* text output encoding, for terminals that support it */
+enum set_encoding_id encoding;
+/* table of encoding names, for output of the setting */
+const char *encoding_names[] = {
+ "default", "iso_8859_1", "iso_8859_2", "iso_8859_15",
+ "cp437", "cp850", "cp852", "cp1250", "koi8r", "koi8u", NULL };
+/* 'set encoding' options */
+const struct gen_table set_encoding_tbl[] =
+{
+ { "def$ault", S_ENC_DEFAULT },
+ { "iso$_8859_1", S_ENC_ISO8859_1 },
+ { "iso_8859_2", S_ENC_ISO8859_2 },
+ { "iso_8859_15", S_ENC_ISO8859_15 },
+ { "cp4$37", S_ENC_CP437 },
+ { "cp850", S_ENC_CP850 },
+ { "cp852", S_ENC_CP852 },
+ { "cp1250", S_ENC_CP1250 },
+ { "koi8$r", S_ENC_KOI8_R },
+ { "koi8$u", S_ENC_KOI8_U },
+ { NULL, S_ENC_INVALID }
+};
+
+const char *arrow_head_names[4] =
+ {"nohead", "head", "backhead", "heads"};
+
+/* HBB 20020225: moved here, from ipc.h, where it never should have
+ * been. */
+#ifdef PIPE_IPC
+/* HBB 20020225: currently not used anywhere outside term.c --> make
+ * it static */
+static SELECT_TYPE_ARG1 ipc_back_fd = IPC_BACK_CLOSED;
+#endif
+
+/* resolution in dpi for converting pixels to size units */
+int gp_resolution = 72;
+
+/* Support for enhanced text mode. Declared extern in term_api.h */
+char enhanced_text[MAX_LINE_LEN+1] = "";
+char *enhanced_cur_text = NULL;
+double enhanced_fontscale = 1.0;
+char enhanced_escape_format[16] = "";
+double enhanced_max_height = 0.0, enhanced_min_height = 0.0;
+/* flag variable to disable enhanced output of filenames, mainly. */
+TBOOLEAN ignore_enhanced_text = FALSE;
+
+/* Internal variables */
+
+/* true if terminal is in graphics mode */
+static TBOOLEAN term_graphics = FALSE;
+
+/* we have suspended the driver, in multiplot mode */
+static TBOOLEAN term_suspended = FALSE;
+
+/* true if? */
+static TBOOLEAN opened_binary = FALSE;
+
+/* true if require terminal to be initialized */
+static TBOOLEAN term_force_init = FALSE;
+
+/* internal pointsize for do_point */
+static double term_pointsize=1;
+
+/* Internal prototypes: */
+
+static void term_suspend __PROTO((void));
+static void term_close_output __PROTO((void));
+static struct termentry *change_term __PROTO((const char *name, int length));
+
+static void null_linewidth __PROTO((double));
+static void do_point __PROTO((unsigned int x, unsigned int y, int number));
+static void do_pointsize __PROTO((double size));
+static void line_and_point __PROTO((unsigned int x, unsigned int y, int number));
+static void do_arrow __PROTO((unsigned int sx, unsigned int sy, unsigned int ex, unsigned int ey, int head));
+
+static void UP_redirect __PROTO((int called));
+
+static int null_text_angle __PROTO((int ang));
+static int null_justify_text __PROTO((enum JUSTIFY just));
+static int null_scale __PROTO((double x, double y));
+static void options_null __PROTO((void));
+static void UNKNOWN_null __PROTO((void));
+static void MOVE_null __PROTO((unsigned int, unsigned int));
+static void LINETYPE_null __PROTO((int));
+static void PUTTEXT_null __PROTO((unsigned int, unsigned int, const char *));
+
+/* Used by terminals and by shared routine parse_term_size() */
+typedef enum {
+ PIXELS,
+ INCHES,
+ CM
+} size_units;
+static size_units parse_term_size __PROTO((float *xsize, float *ysize, size_units def_units));
+
+/*
+ * Bookkeeping and support routine for 'set multiplot layout <rows>, <cols>'
+ * July 2004
+ * Volker Dobler <v.dobler@web.de>
+ */
+
+static void mp_layout_size_and_offset __PROTO((void));
+
+enum set_multiplot_id {
+ S_MULTIPLOT_LAYOUT,
+ S_MULTIPLOT_COLUMNSFIRST, S_MULTIPLOT_ROWSFIRST, S_MULTIPLOT_SCALE,
+ S_MULTIPLOT_DOWNWARDS, S_MULTIPLOT_UPWARDS,
+ S_MULTIPLOT_OFFSET, S_MULTIPLOT_TITLE, S_MULTIPLOT_INVALID
+};
+
+static const struct gen_table set_multiplot_tbl[] =
+{
+ { "lay$out", S_MULTIPLOT_LAYOUT },
+ { "col$umnsfirst", S_MULTIPLOT_COLUMNSFIRST },
+ { "row$sfirst", S_MULTIPLOT_ROWSFIRST },
+ { "down$wards", S_MULTIPLOT_DOWNWARDS },
+ { "up$wards", S_MULTIPLOT_UPWARDS },
+ { "sca$le", S_MULTIPLOT_SCALE },
+ { "off$set", S_MULTIPLOT_OFFSET },
+ { "ti$tle", S_MULTIPLOT_TITLE },
+ { NULL, S_MULTIPLOT_INVALID }
+};
+
+# define MP_LAYOUT_DEFAULT { \
+ FALSE, /* auto_layout */ \
+ 0, 0, /* num_rows, num_cols */ \
+ FALSE, /* row_major */ \
+ TRUE, /* downwards */ \
+ 0, 0, /* act_row, act_col */ \
+ 1, 1, /* xscale, yscale */ \
+ 0, 0, /* xoffset, yoffset */ \
+ 0,0,0,0, /* prev_ sizes and offsets */ \
+ EMPTY_LABELSTRUCT, 0.0 \
+}
+
+static struct {
+ TBOOLEAN auto_layout; /* automatic layout if true */
+ int num_rows; /* number of rows in layout */
+ int num_cols; /* number of columns in layout */
+ TBOOLEAN row_major; /* row major mode if true, column major else */
+ TBOOLEAN downwards; /* prefer downwards or upwards direction */
+ int act_row; /* actual row in layout */
+ int act_col; /* actual column in layout */
+ double xscale; /* factor for horizontal scaling */
+ double yscale; /* factor for vertical scaling */
+ double xoffset; /* horizontal shift */
+ double yoffset; /* horizontal shift */
+ double prev_xsize, prev_ysize, prev_xoffset, prev_yoffset;
+ /* values before 'set multiplot layout' */
+ text_label title; /* goes above complete set of plots */
+ double title_height; /* fractional height reserved for title */
+} mp_layout = MP_LAYOUT_DEFAULT;
+
+
+#ifdef __ZTC__
+char *ztc_init();
+/* #undef TGIF */
+#endif
+
+#ifdef VMS
+char *vms_init();
+void vms_reset();
+void term_mode_tek();
+void term_mode_native();
+void term_pasthru();
+void term_nopasthru();
+void fflush_binary();
+# define FOPEN_BINARY(file) fopen(file, "wb", "rfm=fix", "bls=512", "mrs=512")
+#else /* !VMS */
+# define FOPEN_BINARY(file) fopen(file, "wb")
+#endif /* !VMS */
+
+#if defined(MSDOS) || defined(WIN32) || defined(WIN16)
+# if defined(__DJGPP__) || defined (__TURBOC__)
+# include <io.h>
+# endif
+# include <fcntl.h>
+# ifndef O_BINARY
+# ifdef _O_BINARY
+# define O_BINARY _O_BINARY
+# else
+# define O_BINARY O_BINARY_is_not_defined
+# endif
+# endif
+#endif
+
+#ifdef __EMX__
+#include <io.h>
+#include <fcntl.h>
+#endif
+
+#if defined(__WATCOMC__) || defined(__MSC__)
+# include <io.h> /* for setmode() */
+#endif
+
+/* This is needed because the unixplot library only writes to stdout,
+ * but GNU plotutils libplot.a doesn't */
+#if defined(UNIXPLOT) && !defined(GNUGRAPH)
+static FILE save_stdout;
+#endif
+static int unixplot = 0;
+
+#if defined(MTOS) || defined(ATARI)
+int aesid = -1;
+#endif
+
+#define NICE_LINE 0
+#define POINT_TYPES 6
+
+#ifndef DEFAULTTERM
+# define DEFAULTTERM NULL
+#endif
+
+/* interface to the rest of gnuplot - the rules are getting
+ * too complex for the rest of gnuplot to be allowed in
+ */
+
+#if defined(PIPES)
+static TBOOLEAN output_pipe_open = FALSE;
+#endif /* PIPES */
+
+static void
+term_close_output()
+{
+ FPRINTF((stderr, "term_close_output\n"));
+
+ opened_binary = FALSE;
+
+ if (!outstr) /* ie using stdout */
+ return;
+
+#if defined(PIPES)
+ if (output_pipe_open) {
+ (void) pclose(gpoutfile);
+ output_pipe_open = FALSE;
+ } else
+#endif /* PIPES */
+#ifdef _Windows
+ if (stricmp(outstr, "PRN") == 0)
+ close_printer(gpoutfile);
+ else
+#endif
+ if (gpoutfile != gppsfile)
+ fclose(gpoutfile);
+
+ gpoutfile = stdout; /* Don't dup... */
+ free(outstr);
+ outstr = NULL;
+
+ if (gppsfile)
+ fclose(gppsfile);
+ gppsfile = NULL;
+}
+
+#ifdef OS2
+# define POPEN_MODE ("wb")
+#else
+# define POPEN_MODE ("w")
+#endif
+
+/* assigns dest to outstr, so it must be allocated or NULL
+ * and it must not be outstr itself !
+ */
+void
+term_set_output(char *dest)
+{
+ FILE *f = NULL;
+
+ FPRINTF((stderr, "term_set_output\n"));
+ assert(dest == NULL || dest != outstr);
+
+ if (multiplot) {
+ fputs("In multiplotmode you can't change the output\n", stderr);
+ return;
+ }
+ if (term && term_initialised) {
+ (*term->reset) ();
+ term_initialised = FALSE;
+ /* switch off output to special postscript file (if used) */
+ gppsfile = NULL;
+ }
+ if (dest == NULL) { /* stdout */
+ UP_redirect(4);
+ term_close_output();
+ } else {
+
+#if defined(PIPES)
+ if (*dest == '|') {
+ if ((f = popen(dest + 1, POPEN_MODE)) == (FILE *) NULL)
+ os_error(c_token, "cannot create pipe; output not changed");
+ else
+ output_pipe_open = TRUE;
+ } else {
+#endif /* PIPES */
+
+#ifdef _Windows
+ if (outstr && stricmp(outstr, "PRN") == 0) {
+ /* we can't call open_printer() while printer is open, so */
+ close_printer(gpoutfile); /* close printer immediately if open */
+ gpoutfile = stdout; /* and reset output to stdout */
+ free(outstr);
+ outstr = NULL;
+ }
+ if (stricmp(dest, "PRN") == 0) {
+ if ((f = open_printer()) == (FILE *) NULL)
+ os_error(c_token, "cannot open printer temporary file; output may have changed");
+ } else
+#endif
+
+ {
+#if defined (MSDOS)
+ if (outstr && (0 == stricmp(outstr, dest))) {
+ /* On MSDOS, you cannot open the same file twice and
+ * then close the first-opened one and keep the second
+ * open, it seems. If you do, you get lost clusters
+ * (connection to the first version of the file is
+ * lost, it seems). */
+ /* FIXME: this is not yet safe enough. You can fool it by
+ * specifying the same output file in two different ways
+ * (relative vs. absolute path to file, e.g.) */
+ term_close_output();
+ }
+#endif
+ if (term && (term->flags & TERM_BINARY))
+ f = FOPEN_BINARY(dest);
+ else
+ f = fopen(dest, "w");
+
+ if (f == (FILE *) NULL)
+ os_error(c_token, "cannot open file; output not changed");
+ }
+#if defined(PIPES)
+ }
+#endif
+
+ term_close_output();
+ gpoutfile = f;
+ outstr = dest;
+ opened_binary = (term && (term->flags & TERM_BINARY));
+ UP_redirect(1);
+ }
+}
+
+void
+term_initialise()
+{
+ FPRINTF((stderr, "term_initialise()\n"));
+
+ if (!term)
+ int_error(NO_CARET, "No terminal defined");
+
+ /* check if we have opened the output file in the wrong mode
+ * (text/binary), if set term comes after set output
+ * This was originally done in change_term, but that
+ * resulted in output files being truncated
+ */
+
+ if (outstr && (term->flags & TERM_NO_OUTPUTFILE)) {
+ if (interactive)
+ fprintf(stderr,"Closing %s\n",outstr);
+ term_close_output();
+ }
+
+ if (outstr &&
+ (((term->flags & TERM_BINARY) && !opened_binary) ||
+ ((!(term->flags & TERM_BINARY) && opened_binary)))) {
+ /* this is nasty - we cannot just term_set_output(outstr)
+ * since term_set_output will first free outstr and we
+ * end up with an invalid pointer. I think I would
+ * prefer to defer opening output file until first plot.
+ */
+ char *temp = gp_alloc(strlen(outstr) + 1, "temp file string");
+ if (temp) {
+ FPRINTF((stderr, "term_initialise: reopening \"%s\" as %s\n",
+ outstr, term->flags & TERM_BINARY ? "binary" : "text"));
+ strcpy(temp, outstr);
+ term_set_output(temp); /* will free outstr */
+ if (temp != outstr) {
+ if (temp)
+ free(temp);
+ temp = outstr;
+ }
+ } else
+ fputs("Cannot reopen output file in binary", stderr);
+ /* and carry on, hoping for the best ! */
+ }
+#if defined(MSDOS) || defined (_Windows) || defined(OS2)
+# ifdef _Windows
+ else if (!outstr && (term->flags & TERM_BINARY))
+# else
+ else if (!outstr && !interactive && (term->flags & TERM_BINARY))
+# endif
+ {
+ /* binary to stdout in non-interactive session... */
+ fflush(stdout);
+ setmode(fileno(stdout), O_BINARY);
+ }
+#endif
+
+
+ if (!term_initialised || term_force_init) {
+ FPRINTF((stderr, "- calling term->init()\n"));
+ (*term->init) ();
+ term_initialised = TRUE;
+ }
+}
+
+
+void
+term_start_plot()
+{
+ FPRINTF((stderr, "term_start_plot()\n"));
+
+ if (!term_initialised)
+ term_initialise();
+
+ if (!term_graphics) {
+ FPRINTF((stderr, "- calling term->graphics()\n"));
+ (*term->graphics) ();
+ term_graphics = TRUE;
+ } else if (multiplot && term_suspended) {
+ if (term->resume) {
+ FPRINTF((stderr, "- calling term->resume()\n"));
+ (*term->resume) ();
+ }
+ term_suspended = FALSE;
+ }
+
+ /* Sync point for epslatex text positioning */
+ if (term->layer)
+ (term->layer)(TERM_LAYER_RESET);
+
+ /* Because PostScript plots may be viewed out of order, make sure */
+ /* Each new plot makes no assumption about the previous palette. */
+ if (term->flags & TERM_IS_POSTSCRIPT)
+ invalidate_palette();
+
+ /* Set canvas size to full range of current terminal coordinates */
+ canvas.xleft = 0;
+ canvas.xright = term->xmax;
+ canvas.ybot = 0;
+ canvas.ytop = term->ymax;
+
+}
+
+void
+term_end_plot()
+{
+ FPRINTF((stderr, "term_end_plot()\n"));
+
+ if (!term_initialised)
+ return;
+
+ /* Sync point for epslatex text positioning */
+ if (term->layer)
+ (term->layer)(TERM_LAYER_END_TEXT);
+
+ if (!multiplot) {
+ FPRINTF((stderr, "- calling term->text()\n"));
+ (*term->text) ();
+ term_graphics = FALSE;
+ } else {
+ if (mp_layout.auto_layout) {
+ if (mp_layout.row_major) {
+ mp_layout.act_row++;
+ if (mp_layout.act_row == mp_layout.num_rows ) {
+ mp_layout.act_row = 0;
+ mp_layout.act_col++;
+ if (mp_layout.act_col == mp_layout.num_cols ) {
+ /* int_warn(NO_CARET,"will overplot first plot"); */
+ mp_layout.act_col = 0;
+ }
+ }
+ } else { /* column-major */
+ mp_layout.act_col++;
+ if (mp_layout.act_col == mp_layout.num_cols ) {
+ mp_layout.act_col = 0;
+ mp_layout.act_row++;
+ if (mp_layout.act_row == mp_layout.num_rows ) {
+ /* int_warn(NO_CARET,"will overplot first plot"); */
+ mp_layout.act_col = 0;
+ }
+ }
+ }
+ mp_layout_size_and_offset();
+ }
+ }
+#ifdef VMS
+ if (opened_binary)
+ fflush_binary();
+ else
+#endif /* VMS */
+
+ (void) fflush(gpoutfile);
+
+#ifdef USE_MOUSE
+ recalc_statusline();
+ update_ruler();
+#endif
+}
+
+void
+term_start_multiplot()
+{
+ FPRINTF((stderr, "term_start_multiplot()\n"));
+
+ c_token++;
+ if (multiplot)
+ term_end_multiplot();
+
+ term_start_plot();
+
+ mp_layout.auto_layout = FALSE;
+
+ /* Parse options (new in version 4.1 */
+ while (!END_OF_COMMAND) {
+ struct value a;
+ char *s;
+
+ if (almost_equals(c_token, "ti$tle")) {
+ c_token++;
+ if ((s = try_to_get_string())) {
+ free(mp_layout.title.text);
+ mp_layout.title.text = s;
+ }
+ continue;
+ }
+
+ if (almost_equals(c_token, "lay$out")) {
+ if (mp_layout.auto_layout)
+ int_error(c_token, "too many layout commands");
+ else
+ mp_layout.auto_layout = TRUE;
+
+ c_token++;
+ if (END_OF_COMMAND) {
+ int_error(c_token,"expecting '<num_cols>,<num_rows>'");
+ }
+
+ /* read row,col */
+ mp_layout.num_rows = (int) real(const_express(&a));
+ if (END_OF_COMMAND || !equals(c_token,",") )
+ int_error(c_token, "expecting ', <num_cols>'");
+
+ c_token++;
+ if (END_OF_COMMAND)
+ int_error(c_token, "expecting <num_cols>");
+ mp_layout.num_cols = (int) real(const_express(&a));
+
+ /* remember current values of the plot size */
+ mp_layout.prev_xsize = xsize;
+ mp_layout.prev_ysize = ysize;
+ mp_layout.prev_xoffset = xoffset;
+ mp_layout.prev_yoffset = yoffset;
+
+ mp_layout.act_row = 0;
+ mp_layout.act_col = 0;
+
+ continue;
+ }
+
+ /* The remaining options are only valid for auto-layout mode */
+ if (!mp_layout.auto_layout)
+ int_error(c_token, "only valid as part of an auto-layout command");
+
+ switch(lookup_table(&set_multiplot_tbl[0],c_token)) {
+ case S_MULTIPLOT_COLUMNSFIRST:
+ mp_layout.row_major = TRUE;
+ c_token++;
+ break;
+ case S_MULTIPLOT_ROWSFIRST:
+ mp_layout.row_major = FALSE;
+ c_token++;
+ break;
+ case S_MULTIPLOT_DOWNWARDS:
+ mp_layout.downwards = TRUE;
+ c_token++;
+ break;
+ case S_MULTIPLOT_UPWARDS:
+ mp_layout.downwards = FALSE;
+ c_token++;
+ break;
+ case S_MULTIPLOT_SCALE:
+ c_token++;
+ mp_layout.xscale = real(const_express(&a));
+ mp_layout.yscale = mp_layout.xscale;
+ if (!END_OF_COMMAND && equals(c_token,",") ) {
+ c_token++;
+ if (END_OF_COMMAND) {
+ int_error(c_token, "expecting <yscale>");
+ }
+ mp_layout.yscale = real(const_express(&a));
+ }
+ break;
+ case S_MULTIPLOT_OFFSET:
+ c_token++;
+ mp_layout.xoffset = real(const_express(&a));
+ mp_layout.yoffset = mp_layout.xoffset;
+ if (!END_OF_COMMAND && equals(c_token,",") ) {
+ c_token++;
+ if (END_OF_COMMAND) {
+ int_error(c_token, "expecting <yoffset>");
+ }
+ mp_layout.yoffset = real(const_express(&a));
+ }
+ break;
+ default:
+ int_error(c_token,"invalid or duplicate option");
+ break;
+ }
+ }
+
+ /* If we reach here, then the command has been successfully parsed */
+ multiplot = TRUE;
+
+ /* Place overall title before doing anything else */
+ if (mp_layout.title.text) {
+ double tmpx, tmpy;
+ unsigned int x, y;
+ char *p = mp_layout.title.text;
+
+ map_position_r(&(mp_layout.title.offset), &tmpx, &tmpy, "mp title");
+ x = term->xmax / 2 + tmpx;
+ y = term->ymax - term->v_char + tmpy;;
+
+ ignore_enhanced(mp_layout.title.noenhanced);
+ apply_pm3dcolor(&(mp_layout.title.textcolor), term);
+ write_multiline(x, y, mp_layout.title.text,
+ CENTRE, JUST_TOP, 0, mp_layout.title.font);
+ reset_textcolor(&(mp_layout.title.textcolor), term);
+ ignore_enhanced(FALSE);
+
+ /* Calculate fractional height of title compared to entire page */
+ /* If it would fill the whole page, forget it! */
+ for (y=2; *p; p++)
+ if (*p == '\n')
+ y++;
+ mp_layout.title_height = (double)(y * term->v_char) / (double)term->ymax;
+ if (mp_layout.title_height > 0.9)
+ mp_layout.title_height = 0.05;
+ } else {
+ mp_layout.title_height = 0.0;
+ }
+
+ mp_layout_size_and_offset();
+
+#ifdef USE_MOUSE
+ /* save the state of mouse_setting.on and
+ * disable mouse; call UpdateStatusline()
+ * to turn of eventually statusline */
+ save_mouse_state = mouse_setting.on;
+ mouse_setting.on = 0;
+ UpdateStatusline();
+#endif
+}
+
+void
+term_end_multiplot()
+{
+ FPRINTF((stderr, "term_end_multiplot()\n"));
+ if (!multiplot)
+ return;
+
+ if (term_suspended) {
+ if (term->resume)
+ (*term->resume) ();
+ term_suspended = FALSE;
+ }
+ multiplot = FALSE;
+ /* reset plot size and origin to values before 'set multiplot layout' */
+ if (mp_layout.auto_layout) {
+ xsize = mp_layout.prev_xsize;
+ ysize = mp_layout.prev_ysize;
+ xoffset = mp_layout.prev_xoffset;
+ yoffset = mp_layout.prev_yoffset;
+ }
+ /* reset automatic multiplot layout */
+ mp_layout.auto_layout = FALSE;
+ mp_layout.xscale = mp_layout.yscale = 1.0;
+ mp_layout.xoffset = mp_layout.yoffset = 0.0;
+ if (mp_layout.title.text) {
+ free(mp_layout.title.text);
+ mp_layout.title.text = NULL;
+ }
+
+ term_end_plot();
+#ifdef USE_MOUSE
+ /* restore the state of mouse_setting.on;
+ * call UpdateStatusline() to turn on
+ * eventually statusline */
+ mouse_setting.on = save_mouse_state;
+ UpdateStatusline();
+#endif
+}
+
+
+
+static void
+term_suspend()
+{
+ FPRINTF((stderr, "term_suspend()\n"));
+ if (term_initialised && !term_suspended && term->suspend) {
+ FPRINTF((stderr, "- calling term->suspend()\n"));
+ (*term->suspend) ();
+ term_suspended = TRUE;
+ }
+}
+
+void
+term_reset()
+{
+ FPRINTF((stderr, "term_reset()\n"));
+
+#ifdef USE_MOUSE
+ /* Make sure that ^C will break out of a wait for 'pause mouse' */
+ paused_for_mouse = 0;
+#endif
+
+ if (!term_initialised)
+ return;
+
+ if (term_suspended) {
+ if (term->resume) {
+ FPRINTF((stderr, "- calling term->resume()\n"));
+ (*term->resume) ();
+ }
+ term_suspended = FALSE;
+ }
+ if (term_graphics) {
+ (*term->text) ();
+ term_graphics = FALSE;
+ }
+ if (term_initialised) {
+ (*term->reset) ();
+ term_initialised = FALSE;
+ /* switch off output to special postscript file (if used) */
+ gppsfile = NULL;
+ }
+}
+
+void
+term_apply_lp_properties(struct lp_style_type *lp)
+{
+ /* This function passes all the line and point properties to the
+ * terminal driver and issues the corresponding commands.
+ *
+ * Alas, sometimes it might be necessary to give some help to
+ * this function by explicitly issuing additional '(*term)(...)'
+ * commands.
+ */
+
+ if (lp->pointflag) {
+ /* change points, too
+ * Currently, there is no 'pointtype' function. For points
+ * there is a special function also dealing with (x,y) co-
+ * ordinates.
+ */
+ if (lp->p_size < 0)
+ (*term->pointsize) (pointsize);
+ else
+ (*term->pointsize) (lp->p_size);
+ }
+ /* _first_ set the line width, _then_ set the line type !
+
+ * The linetype might depend on the linewidth in some terminals.
+ */
+ (*term->linewidth) (lp->l_width);
+
+ /* Apply "linetype", which can include both color and dot/dash */
+ (*term->linetype) (lp->l_type);
+ /* Possibly override the linetype color with a fancier colorspec */
+ if (lp->use_palette)
+ apply_pm3dcolor(&lp->pm3d_color, term);
+}
+
+
+
+void
+term_check_multiplot_okay(TBOOLEAN f_interactive)
+{
+ FPRINTF((stderr, "term_multiplot_okay(%d)\n", f_interactive));
+
+ if (!term_initialised)
+ return; /* they've not started yet */
+
+ /* make sure that it is safe to issue an interactive prompt
+ * it is safe if
+ * it is not an interactive read, or
+ * the terminal supports interactive multiplot, or
+ * we are not writing to stdout and terminal doesn't
+ * refuse multiplot outright
+ */
+ if (!f_interactive || (term->flags & TERM_CAN_MULTIPLOT) ||
+ ((gpoutfile != stdout) && !(term->flags & TERM_CANNOT_MULTIPLOT))
+ ) {
+ /* it's okay to use multiplot here, but suspend first */
+ term_suspend();
+ return;
+ }
+ /* uh oh: they're not allowed to be in multiplot here */
+
+ term_end_multiplot();
+
+ /* at this point we know that it is interactive and that the
+ * terminal can either only do multiplot when writing to
+ * to a file, or it does not do multiplot at all
+ */
+
+ if (term->flags & TERM_CANNOT_MULTIPLOT)
+ int_error(NO_CARET, "This terminal does not support multiplot");
+ else
+ int_error(NO_CARET, "Must set output to a file or put all multiplot commands on one input line");
+}
+
+
+void
+write_multiline(
+ unsigned int x, unsigned y,
+ char *text,
+ JUSTIFY hor, /* horizontal ... */
+ VERT_JUSTIFY vert, /* ... and vertical just - text in hor direction despite angle */
+ int angle, /* assume term has already been set for this */
+ const char *font) /* NULL or "" means use default */
+{
+ struct termentry *t = term;
+ char *p = text;
+
+ if (!p)
+ return;
+
+ /* EAM 9-Feb-2003 - Set font before calculating sizes */
+ if (font && *font && t->set_font)
+ (*t->set_font) (font);
+
+ if (vert != JUST_TOP) {
+ /* count lines and adjust y */
+ int lines = 0; /* number of linefeeds - one fewer than lines */
+ while (*p) {
+ if (*p++ == '\n')
+ ++lines;
+ }
+ if (angle)
+ x -= (vert * lines * t->v_char) / 2;
+ else
+ y += (vert * lines * t->v_char) / 2;
+ }
+
+ for (;;) { /* we will explicitly break out */
+
+ if ((text != NULL) && (p = strchr(text, '\n')) != NULL)
+ *p = 0; /* terminate the string */
+
+ if ((*t->justify_text) (hor)) {
+ if (on_page(x, y))
+ (*t->put_text) (x, y, text);
+ } else {
+ int fix = hor * t->h_char * estimate_strlen(text) / 2;
+ if (angle) {
+ if (on_page(x, y - fix))
+ (*t->put_text) (x, y - fix, text);
+ }
+ else {
+ if (on_page(x - fix, y))
+ (*t->put_text) (x - fix, y, text);
+ }
+ }
+ if (angle == TEXT_VERTICAL)
+ x += t->v_char;
+ else if (-angle == TEXT_VERTICAL)
+ x -= t->v_char;
+ else
+ y -= t->v_char;
+
+ if (!p)
+ break;
+ else {
+ /* put it back */
+ *p = '\n';
+ }
+
+ text = p + 1;
+ } /* unconditional branch back to the for(;;) - just a goto ! */
+
+ if (font && *font && t->set_font)
+ (*t->set_font) ("");
+
+}
+
+
+static void
+do_point(unsigned int x, unsigned int y, int number)
+{
+ int htic, vtic;
+ struct termentry *t = term;
+
+ if (number < 0) { /* do dot */
+ (*t->move) (x, y);
+ (*t->vector) (x, y);
+ return;
+ }
+ number %= POINT_TYPES;
+ /* should be in term_tbl[] in later version */
+ htic = (term_pointsize * t->h_tic / 2);
+ vtic = (term_pointsize * t->v_tic / 2);
+
+ /* point types 1..4 are same as in postscript, png and x11
+ point types 5..6 are "similar"
+ (note that (number) equals (pointtype-1)
+ */
+ switch (number) {
+ case 4: /* do diamond */
+ (*t->move) (x - htic, y);
+ (*t->vector) (x, y - vtic);
+ (*t->vector) (x + htic, y);
+ (*t->vector) (x, y + vtic);
+ (*t->vector) (x - htic, y);
+ (*t->move) (x, y);
+ (*t->vector) (x, y);
+ break;
+ case 0: /* do plus */
+ (*t->move) (x - htic, y);
+ (*t->vector) (x - htic, y);
+ (*t->vector) (x + htic, y);
+ (*t->move) (x, y - vtic);
+ (*t->vector) (x, y - vtic);
+ (*t->vector) (x, y + vtic);
+ break;
+ case 3: /* do box */
+ (*t->move) (x - htic, y - vtic);
+ (*t->vector) (x - htic, y - vtic);
+ (*t->vector) (x + htic, y - vtic);
+ (*t->vector) (x + htic, y + vtic);
+ (*t->vector) (x - htic, y + vtic);
+ (*t->vector) (x - htic, y - vtic);
+ (*t->move) (x, y);
+ (*t->vector) (x, y);
+ break;
+ case 1: /* do X */
+ (*t->move) (x - htic, y - vtic);
+ (*t->vector) (x - htic, y - vtic);
+ (*t->vector) (x + htic, y + vtic);
+ (*t->move) (x - htic, y + vtic);
+ (*t->vector) (x - htic, y + vtic);
+ (*t->vector) (x + htic, y - vtic);
+ break;
+ case 5: /* do triangle */
+ (*t->move) (x, y + (4 * vtic / 3));
+ (*t->vector) (x - (4 * htic / 3), y - (2 * vtic / 3));
+ (*t->vector) (x + (4 * htic / 3), y - (2 * vtic / 3));
+ (*t->vector) (x, y + (4 * vtic / 3));
+ (*t->move) (x, y);
+ (*t->vector) (x, y);
+ break;
+ case 2: /* do star */
+ (*t->move) (x - htic, y);
+ (*t->vector) (x - htic, y);
+ (*t->vector) (x + htic, y);
+ (*t->move) (x, y - vtic);
+ (*t->vector) (x, y - vtic);
+ (*t->vector) (x, y + vtic);
+ (*t->move) (x - htic, y - vtic);
+ (*t->vector) (x - htic, y - vtic);
+ (*t->vector) (x + htic, y + vtic);
+ (*t->move) (x - htic, y + vtic);
+ (*t->vector) (x - htic, y + vtic);
+ (*t->vector) (x + htic, y - vtic);
+ break;
+ }
+}
+
+static void
+do_pointsize(double size)
+{
+ term_pointsize = (size >= 0 ? size : 1);
+}
+
+
+/*
+ * general point routine
+ */
+static void
+line_and_point(unsigned int x, unsigned int y, int number)
+{
+ /* temporary(?) kludge to allow terminals with bad linetypes
+ to make nice marks */
+
+ (*term->linetype) (NICE_LINE);
+ do_point(x, y, number);
+}
+
+/*
+ * general arrow routine
+ *
+ * I set the angle between the arrowhead and the line 15 degree.
+ * The length of arrowhead varies depending on the line length
+ * within the the range [0.3*(the-tic-length), 2*(the-tic-length)].
+ * No head is printed if the arrow length is zero.
+ *
+ * Yasu-hiro Yamazaki(hiro@rainbow.physics.utoronto.ca)
+ * Jul 1, 1993
+ */
+
+#define COS15 (0.96593) /* cos of 15 degree */
+#define SIN15 (0.25882) /* sin of 15 degree */
+
+#define HEAD_LONG_LIMIT (2.0) /* long limit of arrowhead length */
+#define HEAD_SHORT_LIMIT (0.3) /* short limit of arrowhead length */
+ /* their units are the "tic" length */
+
+#define HEAD_COEFF (0.3) /* default value of head/line length ratio */
+
+int curr_arrow_headlength; /* access head length + angle without changing API */
+double curr_arrow_headangle; /* angle in degrees */
+double curr_arrow_headbackangle; /* angle in degrees */
+int curr_arrow_headfilled; /* arrow head filled or not */
+
+static void
+do_arrow(
+ unsigned int usx, unsigned int usy, /* start point */
+ unsigned int uex, unsigned int uey, /* end point (point of arrowhead) */
+ int headstyle)
+{
+ /* Clipping and angle calculations do not work if coords are unsigned! */
+ int sx = (int)usx;
+ int sy = (int)usy;
+ int ex = (int)uex;
+ int ey = (int)uey;
+
+ struct termentry *t = term;
+ float len_tic = ((double) (t->h_tic + t->v_tic)) / 2.0;
+ /* average of tic sizes */
+ /* (dx,dy) : vector from end to start */
+ double dx = sx - ex;
+ double dy = sy - ey;
+ double len_arrow = sqrt(dx * dx + dy * dy);
+ gpiPoint filledhead[5];
+ int xm = 0, ym = 0;
+ BoundingBox *clip_save;
+ t_arrow_head head = (t_arrow_head)((headstyle < 0) ? -headstyle : headstyle);
+ /* negative headstyle means draw heads only, no shaft */
+
+ /* FIXME: The plan is to migrate calling routines to call via */
+ /* draw_clip_arrow() in which case we would not need to clip again here. */
+ clip_save = clip_area;
+ if (term->flags & TERM_CAN_CLIP)
+ clip_area = NULL;
+ else
+ clip_area = &canvas;
+
+ /* Calculate and draw arrow heads.
+ * Draw no head for arrows with length = 0, or, to be more specific,
+ * length < DBL_EPSILON, because len_arrow will almost always be != 0.
+ */
+ if ((head != NOHEAD) && fabs(len_arrow) >= DBL_EPSILON) {
+ int x1, y1, x2, y2;
+ if (curr_arrow_headlength <= 0) {
+ /* arrow head with the default size */
+ /* now calc the head_coeff */
+ double coeff_shortest = len_tic * HEAD_SHORT_LIMIT / len_arrow;
+ double coeff_longest = len_tic * HEAD_LONG_LIMIT / len_arrow;
+ double head_coeff = GPMAX(coeff_shortest,
+ GPMIN(HEAD_COEFF, coeff_longest));
+ /* we put the arrowhead marks at 15 degrees to line */
+ x1 = (int) ((COS15 * dx - SIN15 * dy) * head_coeff);
+ y1 = (int) ((SIN15 * dx + COS15 * dy) * head_coeff);
+ x2 = (int) ((COS15 * dx + SIN15 * dy) * head_coeff);
+ y2 = (int) ((-SIN15 * dx + COS15 * dy) * head_coeff);
+ /* backangle defaults to 90 deg */
+ xm = (int) ((x1 + x2)/2);
+ ym = (int) ((y1 + y2)/2);
+ } else {
+ /* the arrow head with the length + angle specified explicitly */
+ double alpha = curr_arrow_headangle * DEG2RAD;
+ double beta = curr_arrow_headbackangle * DEG2RAD;
+ double phi = atan2(-dy,-dx); /* azimuthal angle of the vector */
+ double backlen = curr_arrow_headlength * sin(alpha) / sin(beta);
+ double dx2, dy2;
+ /* anticlock-wise head segment */
+ x1 = -(int)(curr_arrow_headlength * cos( alpha - phi ));
+ y1 = (int)(curr_arrow_headlength * sin( alpha - phi ));
+ /* clock-wise head segment */
+ dx2 = -curr_arrow_headlength * cos( phi + alpha );
+ dy2 = -curr_arrow_headlength * sin( phi + alpha );
+ x2 = (int) (dx2);
+ y2 = (int) (dy2);
+ /* back point */
+ xm = (int) (dx2 + backlen * cos( phi + beta ));
+ ym = (int) (dy2 + backlen * sin( phi + beta ));
+ }
+
+ if (head & END_HEAD) {
+ if (curr_arrow_headfilled==2 && !clip_point(ex,ey)) {
+ /* draw filled forward arrow head */
+ filledhead[0].x = ex + xm;
+ filledhead[0].y = ey + ym;
+ filledhead[1].x = ex + x1;
+ filledhead[1].y = ey + y1;
+ filledhead[2].x = ex;
+ filledhead[2].y = ey;
+ filledhead[3].x = ex + x2;
+ filledhead[3].y = ey + y2;
+ filledhead[4].x = ex + xm;
+ filledhead[4].y = ey + ym;
+ filledhead->style = FS_OPAQUE;
+ if (t->filled_polygon)
+ (*t->filled_polygon) (5, filledhead);
+ }
+ /* draw outline of forward arrow head */
+ if (clip_point(ex,ey))
+ ;
+ else if (curr_arrow_headfilled!=0) {
+ draw_clip_line(ex+xm, ey+ym, ex+x1, ey+y1);
+ draw_clip_line(ex+x1, ey+y1, ex, ey);
+ draw_clip_line(ex, ey, ex+x2, ey+y2);
+ draw_clip_line(ex+x2, ey+y2, ex+xm, ey+ym);
+ } else {
+ draw_clip_line(ex+x1, ey+y1, ex, ey);
+ draw_clip_line(ex, ey, ex+x2, ey+y2);
+ }
+ }
+
+ /* backward arrow head */
+ if ((head & BACKHEAD) && !clip_point(sx,sy)) {
+ if (curr_arrow_headfilled==2) {
+ /* draw filled backward arrow head */
+ filledhead[0].x = sx - xm;
+ filledhead[0].y = sy - ym;
+ filledhead[1].x = sx - x1;
+ filledhead[1].y = sy - y1;
+ filledhead[2].x = sx;
+ filledhead[2].y = sy;
+ filledhead[3].x = sx - x2;
+ filledhead[3].y = sy - y2;
+ filledhead[4].x = sx - xm;
+ filledhead[4].y = sy - ym;
+ filledhead->style = FS_OPAQUE;
+ if (t->filled_polygon)
+ (*t->filled_polygon) (5, filledhead);
+ }
+ /* draw outline of backward arrow head */
+ if (curr_arrow_headfilled!=0) {
+ draw_clip_line(sx-xm, sy-ym, sx-x2, sy-y2);
+ draw_clip_line(sx-x2, sy-y2, sx, sy);
+ draw_clip_line(sx, sy, sx-x1, sy-y1);
+ draw_clip_line(sx-x1, sy-y1, sx-xm, sy-ym);
+ } else {
+ draw_clip_line(sx-x2, sy-y2, sx, sy);
+ draw_clip_line(sx, sy, sx-x1, sy-y1);
+ }
+ }
+ }
+
+ /* Draw the line for the arrow. */
+ if (headstyle >= 0) {
+ if ((head & BACKHEAD)
+ && (fabs(len_arrow) >= DBL_EPSILON) && (curr_arrow_headfilled!=0) ) {
+ sx -= xm;
+ sy -= ym;
+ }
+ if ((head & END_HEAD)
+ && (fabs(len_arrow) >= DBL_EPSILON) && (curr_arrow_headfilled!=0) ) {
+ ex += xm;
+ ey += ym;
+ }
+ if (clip_line(&sx, &sy, &ex, &ey))
+ draw_clip_line(sx, sy, ex, ey);
+ }
+
+ /* Restore previous clipping box */
+ clip_area = clip_save;
+
+}
+
+#define TERM_PROTO
+#define TERM_BODY
+#define TERM_PUBLIC static
+
+#include "term.h"
+
+#undef TERM_PROTO
+#undef TERM_BODY
+#undef TERM_PUBLIC
+
+
+/* Dummy functions for unavailable features */
+/* return success if they asked for default - this simplifies code
+ * where param is passed as a param. Client can first pass it here,
+ * and only if it fails do they have to see what was trying to be done
+ */
+
+/* change angle of text. 0 is horizontal left to right.
+ * 1 is vertical bottom to top (90 deg rotate)
+ */
+static int
+null_text_angle(int ang)
+{
+ return (ang == 0);
+}
+
+/* change justification of text.
+ * modes are LEFT (flush left), CENTRE (centred), RIGHT (flush right)
+ */
+static int
+null_justify_text(enum JUSTIFY just)
+{
+ return (just == LEFT);
+}
+
+
+/* Change scale of plot.
+ * Parameters are x,y scaling factors for this plot.
+ * Some terminals (eg latex) need to do scaling themselves.
+ */
+static int
+null_scale(double x, double y)
+{
+ (void) x; /* avoid -Wunused warning */
+ (void) y;
+ return FALSE; /* can't be done */
+}
+
+/* HBB 990829: unused --> commented out */
+#if 0
+int
+do_scale(double x, double y)
+{
+ return TRUE; /* can be done */
+}
+#endif /* commented out */
+
+static void
+options_null()
+{
+ term_options[0] = '\0'; /* we have no options */
+}
+
+static void
+UNKNOWN_null()
+{
+}
+
+static void
+MOVE_null(unsigned int x, unsigned int y)
+{
+ (void) x; /* avoid -Wunused warning */
+ (void) y;
+}
+
+static void
+LINETYPE_null(int t)
+{
+ (void) t; /* avoid -Wunused warning */
+}
+
+static void
+PUTTEXT_null(unsigned int x, unsigned int y, const char *s)
+{
+ (void) s; /* avoid -Wunused warning */
+ (void) x;
+ (void) y;
+}
+
+
+static void
+null_linewidth(double s)
+{
+ (void) s; /* avoid -Wunused warning */
+}
+
+
+/* cast to get rid of useless warnings about UNKNOWN_null */
+/* HBB 20040619: unused --- commented out */
+/* typedef void (*void_fp) __PROTO((void)); */
+
+
+/* setup the magic macros to compile in the right parts of the
+ * terminal drivers included by term.h
+ */
+#define TERM_TABLE
+#define TERM_TABLE_START(x) ,{
+#define TERM_TABLE_END(x) }
+
+
+/*
+ * term_tbl[] contains an entry for each terminal. "unknown" must be the
+ * first, since term is initialized to 0.
+ */
+static struct termentry term_tbl[] =
+{
+ {"unknown", "Unknown terminal type - not a plotting device",
+ 100, 100, 1, 1,
+ 1, 1, options_null, UNKNOWN_null, UNKNOWN_null,
+ UNKNOWN_null, null_scale, UNKNOWN_null, MOVE_null, MOVE_null,
+ LINETYPE_null, PUTTEXT_null}
+
+#include "term.h"
+
+};
+
+#define TERMCOUNT (sizeof(term_tbl) / sizeof(term_tbl[0]))
+
+#if 0 /* UNUSED */
+/* mainly useful for external code */
+int
+term_count()
+{
+ return TERMCOUNT;
+}
+#endif
+
+void
+list_terms()
+{
+ int i;
+ char *line_buffer = gp_alloc(BUFSIZ, "list_terms");
+ int sort_idxs[TERMCOUNT];
+
+ /* sort terminal types alphabetically */
+ for( i = 0; i < TERMCOUNT; i++ )
+ sort_idxs[i] = i;
+ qsort( sort_idxs, TERMCOUNT, sizeof(int), termcomp );
+ /* now sort_idxs[] contains the sorted indices */
+
+ StartOutput();
+ strcpy(line_buffer, "\nAvailable terminal types:\n");
+ OutLine(line_buffer);
+
+ for (i = 0; i < TERMCOUNT; i++) {
+ sprintf(line_buffer, " %15s %s\n",
+ term_tbl[sort_idxs[i]].name,
+ term_tbl[sort_idxs[i]].description);
+ OutLine(line_buffer);
+ }
+
+ EndOutput();
+ free(line_buffer);
+}
+
+static int
+termcomp(const generic *arga, const generic *argb)
+{
+ const int *a = arga;
+ const int *b = argb;
+
+ return( strcasecmp( term_tbl[*a].name, term_tbl[*b].name ) );
+}
+
+/* set_term: get terminal number from name on command line
+ * will change 'term' variable if successful
+ */
+struct termentry *
+set_term(int c_token_arg)
+{
+ struct termentry *t = NULL;
+ char *input_name;
+
+ if (!token[c_token_arg].is_token)
+ int_error(c_token_arg, "terminal name expected");
+ input_name = gp_input_line + token[c_token_arg].start_index;
+ t = change_term(input_name, token[c_token_arg].length);
+ if (!t)
+ int_error(c_token_arg, "unknown or ambiguous terminal type; type just 'set terminal' for a list");
+
+ /* otherwise the type was changed */
+
+ return (t);
+}
+
+/* change_term: get terminal number from name and set terminal type
+ *
+ * returns NULL for unknown or ambiguous, otherwise is terminal
+ * driver pointer
+ */
+static struct termentry *
+change_term(const char *origname, int length)
+{
+ int i;
+ struct termentry *t = NULL;
+ TBOOLEAN ambiguous = FALSE;
+
+ /* For backwards compatibility only */
+ char *name = (char *)origname;
+ if (!strncmp(origname,"X11",length)) {
+ name = "x11";
+ length = 3;
+ }
+
+ for (i = 0; i < TERMCOUNT; i++) {
+ if (!strncmp(name, term_tbl[i].name, length)) {
+ if (t)
+ ambiguous = TRUE;
+ t = term_tbl + i;
+ /* Exact match is always accepted */
+ if (length == strlen(term_tbl[i].name)) {
+ ambiguous = FALSE;
+ break;
+ }
+ }
+ }
+
+ if (!t || ambiguous)
+ return (NULL);
+
+ /* Success: set terminal type now */
+
+ term = t;
+ term_initialised = FALSE;
+
+ if (term->scale != null_scale)
+ fputs("Warning: scale interface is not null_scale - may not work with multiplot\n", stderr);
+
+ /* check that optional fields are initialised to something */
+ if (term->text_angle == 0)
+ term->text_angle = null_text_angle;
+ if (term->justify_text == 0)
+ term->justify_text = null_justify_text;
+ if (term->point == 0)
+ term->point = do_point;
+ if (term->arrow == 0)
+ term->arrow = do_arrow;
+ if (term->pointsize == 0)
+ term->pointsize = do_pointsize;
+ if (term->linewidth == 0)
+ term->linewidth = null_linewidth;
+
+ /* Special handling for unixplot term type */
+ if (!strncmp("unixplot", term->name, 8)) {
+ UP_redirect(2); /* Redirect actual stdout for unixplots */
+ } else if (unixplot) {
+ UP_redirect(3); /* Put stdout back together again. */
+ }
+ if (interactive)
+ fprintf(stderr, "Terminal type set to '%s'\n", term->name);
+
+ /* Invalidate any terminal-specific structures that may be active */
+ invalidate_palette();
+
+ return (t);
+}
+
+/*
+ * Routine to detect what terminal is being used (or do anything else
+ * that would be nice). One anticipated (or allowed for) side effect
+ * is that the global ``term'' may be set.
+ * The environment variable GNUTERM is checked first; if that does
+ * not exist, then the terminal hardware is checked, if possible,
+ * and finally, we can check $TERM for some kinds of terminals.
+ * A default can be set with -DDEFAULTTERM=myterm in the Makefile
+ * or #define DEFAULTTERM myterm in term.h
+ */
+/* thanks to osupyr!alden (Dave Alden) for the original GNUTERM code */
+void
+init_terminal()
+{
+ char *term_name = DEFAULTTERM;
+#if (defined(__TURBOC__) && defined(MSDOS) && !defined(_Windows)) || defined(NEXT) || defined(SUN) || defined(X11)
+ char *env_term = NULL; /* from TERM environment var */
+#endif
+#ifdef X11
+ char *display = NULL;
+#endif
+ char *gnuterm = NULL;
+
+ /* GNUTERM environment variable is primary */
+ gnuterm = getenv("GNUTERM");
+ if (gnuterm != (char *) NULL) {
+ term_name = gnuterm;
+ } else {
+
+#ifdef __ZTC__
+ term_name = ztc_init();
+#endif
+
+#ifdef VMS
+ term_name = vms_init();
+#endif /* VMS */
+
+#ifdef NEXT
+ env_term = getenv("TERM");
+ if (term_name == (char *) NULL
+ && env_term != (char *) NULL && strcmp(env_term, "next") == 0)
+ term_name = "next";
+#endif /* NeXT */
+
+#ifdef __BEOS__
+ env_term = getenv("TERM");
+ if (term_name == (char *) NULL
+ && env_term != (char *) NULL && strcmp(env_term, "beterm") == 0)
+ term_name = "be";
+#endif /* BeOS */
+
+#ifdef SUN
+ env_term = getenv("TERM"); /* try $TERM */
+ if (term_name == (char *) NULL
+ && env_term != (char *) NULL && strcmp(env_term, "sun") == 0)
+ term_name = "sun";
+#endif /* SUN */
+
+#ifdef WXWIDGETS
+ if (term_name == (char *) NULL)
+ term_name = "wxt";
+#endif
+
+#ifdef _Windows
+ /* let the wxWidgets terminal be the default when available */
+ if (term_name == (char *) NULL)
+ term_name = "win";
+#endif /* _Windows */
+
+#ifdef GPR
+ /* find out whether stdout is a DM pad. See term/gpr.trm */
+ if (gpr_isa_pad())
+ term_name = "gpr";
+#else
+# ifdef APOLLO
+ /* find out whether stdout is a DM pad. See term/apollo.trm */
+ if (apollo_isa_pad())
+ term_name = "apollo";
+# endif /* APOLLO */
+#endif /* GPR */
+
+#if defined(__APPLE__) && defined(__MACH__) && defined(HAVE_LIBAQUATERM)
+ /* Mac OS X with AquaTerm installed */
+ term_name = "aqua";
+#endif
+
+#ifdef X11
+ env_term = getenv("TERM"); /* try $TERM */
+ if (term_name == (char *) NULL
+ && env_term != (char *) NULL && strcmp(env_term, "xterm") == 0)
+ term_name = "x11";
+ display = getenv("DISPLAY");
+ if (term_name == (char *) NULL && display != (char *) NULL)
+ term_name = "x11";
+ if (X11_Display)
+ term_name = "x11";
+#endif /* x11 */
+
+#ifdef AMIGA
+ term_name = "amiga";
+#endif
+
+#if defined(ATARI) || defined(MTOS)
+ term_name = "atari";
+#endif
+
+#ifdef UNIXPC
+ if (iswind() == 0) {
+ term_name = "unixpc";
+ }
+#endif /* unixpc */
+
+#ifdef CGI
+ if (getenv("CGIDISP") || getenv("CGIPRNT"))
+ term_name = "cgi";
+#endif /*CGI */
+
+#ifdef DJGPP
+ term_name = "svga";
+#endif
+
+#ifdef GRASS
+ term_name = "grass";
+#endif
+
+#ifdef OS2
+/* amai: Note that we do some checks above and now overwrite any
+ results. Perhaps we may disable checks above!? */
+#ifdef X11
+/* WINDOWID is set in sessions like xterm, etc.
+ DISPLAY is also mandatory. */
+ env_term = getenv("WINDOWID");
+ display = getenv("DISPLAY");
+ if ((env_term != (char *) NULL) && (display != (char *) NULL))
+ term_name = "x11";
+ else
+#endif /* X11 */
+ term_name = "pm";
+#endif /*OS2 */
+
+/* set linux terminal only if LINUX_setup was successfull, if we are on X11
+ LINUX_setup has failed, also if we are logged in by network */
+#ifdef LINUXVGA
+ if (LINUX_graphics_allowed)
+#ifdef VGAGL
+ term_name = "vgagl";
+#else
+ term_name = "linux";
+#endif
+#endif /* LINUXVGA */
+ }
+
+ /* We have a name, try to set term type */
+ if (term_name != NULL && *term_name != '\0') {
+ int namelength = strlen(term_name);
+#ifdef GP_STRING_VARS
+ struct udvt_entry *name = add_udv_by_name("GNUTERM");
+ Gstring(&name->udv_value, gp_strdup(term_name));
+ name->udv_undef = FALSE;
+#endif
+ if (strchr(term_name,' '))
+ namelength = strchr(term_name,' ') - term_name;
+
+ /* Force the terminal to initialize default fonts, etc. This prevents */
+ /* segfaults and other strangeness if you set GNUTERM to "post" or */
+ /* "png" for example. However, calling X11_options() is expensive due */
+ /* to the fork+execute of gnuplot_x11 and x11 can tolerate not being */
+ /* initialized until later. */
+ /* Note that gp_input_line[] is blank at this point. */
+ if (change_term(term_name, namelength)) {
+ if (strcmp(term->name,"x11"))
+ term->options();
+ return;
+ }
+ fprintf(stderr, "Unknown or ambiguous terminal name '%s'\n", term_name);
+ }
+ change_term("unknown", 7);
+}
+
+
+#ifdef __ZTC__
+char *
+ztc_init()
+{
+ int g_mode;
+ char *term_name = NULL;
+
+ g_mode = fg_init();
+
+ switch (g_mode) {
+ case FG_NULL:
+ fputs("Graphics card not detected or not supported.\n", stderr);
+ exit(1);
+ case FG_HERCFULL:
+ term_name = "hercules";
+ break;
+ case FG_EGAMONO:
+ term_name = "egamono";
+ break;
+ case FG_EGAECD:
+ term_name = "egalib";
+ break;
+ case FG_VGA11:
+ term_name = "vgamono";
+ break;
+ case FG_VGA12:
+ term_name = "vgalib";
+ break;
+ case FG_VESA6A:
+ term_name = "svgalib";
+ break;
+ case FG_VESA5:
+ term_name = "ssvgalib";
+ break;
+ }
+ fg_term();
+ return (term_name);
+}
+#endif /* __ZTC__ */
+
+
+/*
+ * Unixplot can't really write to gpoutfile--it wants to write to stdout.
+ * This is normally ok, but the original design of gnuplot gives us
+ * little choice. Originally users of unixplot had to anticipate
+ * their needs and redirect all I/O to a file... Not very gnuplot-like.
+ *
+ * caller: 1 - called from SET OUTPUT "FOO.OUT"
+ * 2 - called from SET TERM UNIXPLOT
+ * 3 - called from SET TERM other
+ * 4 - called from SET OUTPUT
+ */
+static void
+UP_redirect(int caller)
+{
+#if defined(UNIXPLOT) && !defined(GNUGRAPH)
+ switch (caller) {
+ case 1:
+ /* Don't save, just replace stdout w/gpoutfile (save was already done). */
+ if (unixplot)
+ *(stdout) = *(gpoutfile); /* Copy FILE structure */
+ break;
+ case 2:
+ if (!unixplot) {
+ fflush(stdout);
+ save_stdout = *(stdout);
+ *(stdout) = *(gpoutfile); /* Copy FILE structure */
+ unixplot = 1;
+ }
+ break;
+ case 3:
+ /* New terminal in use--put stdout back to original. */
+ /* closepl(); */ /* This is called by the term. */
+ fflush(stdout);
+ *(stdout) = save_stdout; /* Copy FILE structure */
+ unixplot = 0;
+ break;
+ case 4:
+ /* User really wants to go to normal output... */
+ if (unixplot) {
+ fflush(stdout);
+ *(stdout) = save_stdout; /* Copy FILE structure */
+ }
+ break;
+ } /* switch() */
+#else /* !UNIXPLOT || GNUGRAPH */
+ (void) caller; /* avoid -Wunused warning */
+#endif /* !UNIXPLOT || GNUGRAPH */
+}
+
+/* test terminal by drawing border and text */
+/* called from command test */
+void
+test_term()
+{
+ struct termentry *t = term;
+ const char *str;
+ int x, y, xl, yl, i;
+ unsigned int xmax_t, ymax_t;
+ char label[MAX_ID_LEN];
+ int key_entry_height;
+ int p_width;
+
+ term_start_plot();
+ screen_ok = FALSE;
+ xmax_t = (unsigned int) (t->xmax * xsize);
+ ymax_t = (unsigned int) (t->ymax * ysize);
+
+ p_width = pointsize * t->h_tic;
+ key_entry_height = pointsize * t->v_tic * 1.25;
+ if (key_entry_height < t->v_char)
+ key_entry_height = t->v_char;
+
+ /* Sync point for epslatex text positioning */
+ if (term->layer)
+ (term->layer)(TERM_LAYER_FRONTTEXT);
+
+ /* border linetype */
+ (*t->linewidth) (1.0);
+ (*t->linetype) (LT_BLACK);
+ (*t->move) (0, 0);
+ (*t->vector) (xmax_t - 1, 0);
+ (*t->vector) (xmax_t - 1, ymax_t - 1);
+ (*t->vector) (0, ymax_t - 1);
+ (*t->vector) (0, 0);
+ (*t->linetype)(0);
+ (void) (*t->justify_text) (LEFT);
+ (*t->put_text) (t->h_char * 5, ymax_t - t->v_char * 1.5, "Terminal Test");
+#ifdef USE_MOUSE
+ if (t->set_ruler) {
+ (*t->put_text) (t->h_char * 5, ymax_t - t->v_char * 3, "Mouse and hotkeys are supported, hit: h r m 6");
+ }
+#endif
+ (*t->linetype)(LT_BLACK);
+ (*t->linetype) (LT_AXIS);
+ (*t->move) (xmax_t / 2, 0);
+ (*t->vector) (xmax_t / 2, ymax_t - 1);
+ (*t->move) (0, ymax_t / 2);
+ (*t->vector) (xmax_t - 1, ymax_t / 2);
+ /* test width and height of characters */
+ (*t->linetype) (3);
+ (*t->move) (xmax_t / 2 - t->h_char * 10, ymax_t / 2 + t->v_char / 2);
+ (*t->vector) (xmax_t / 2 + t->h_char * 10, ymax_t / 2 + t->v_char / 2);
+ (*t->vector) (xmax_t / 2 + t->h_char * 10, ymax_t / 2 - t->v_char / 2);
+ (*t->vector) (xmax_t / 2 - t->h_char * 10, ymax_t / 2 - t->v_char / 2);
+ (*t->vector) (xmax_t / 2 - t->h_char * 10, ymax_t / 2 + t->v_char / 2);
+ (*t->put_text) (xmax_t / 2 - t->h_char * 10, ymax_t / 2,
+ "12345678901234567890");
+ (*t->put_text) (xmax_t / 2 - t->h_char * 10, ymax_t / 2 + t->v_char * 1.4,
+ "test of character width:");
+ (*t->linetype) (LT_BLACK);
+ /* test justification */
+ (void) (*t->justify_text) (LEFT);
+ (*t->put_text) (xmax_t / 2, ymax_t / 2 + t->v_char * 6, "left justified");
+ str = "centre+d text";
+ if ((*t->justify_text) (CENTRE))
+ (*t->put_text) (xmax_t / 2,
+ ymax_t / 2 + t->v_char * 5, str);
+ else
+ (*t->put_text) (xmax_t / 2 - strlen(str) * t->h_char / 2,
+ ymax_t / 2 + t->v_char * 5, str);
+ str = "right justified";
+ if ((*t->justify_text) (RIGHT))
+ (*t->put_text) (xmax_t / 2,
+ ymax_t / 2 + t->v_char * 4, str);
+ else
+ (*t->put_text) (xmax_t / 2 - strlen(str) * t->h_char,
+ ymax_t / 2 + t->v_char * 4, str);
+ /* test text angle */
+ (*t->linetype)(1);
+ str = "rotated ce+ntred text";
+ if ((*t->text_angle) (TEXT_VERTICAL)) {
+ if ((*t->justify_text) (CENTRE))
+ (*t->put_text) (t->v_char,
+ ymax_t / 2, str);
+ else
+ (*t->put_text) (t->v_char,
+ ymax_t / 2 - strlen(str) * t->h_char / 2, str);
+ (*t->justify_text) (LEFT);
+ str = " rotated by +45 deg";
+ (*t->text_angle)(45);
+ (*t->put_text)(t->v_char * 3, ymax_t / 2, str);
+ (*t->justify_text) (LEFT);
+ str = " rotated by -45 deg";
+ (*t->text_angle)(-45);
+ (*t->put_text)(t->v_char * 2, ymax_t / 2, str);
+#ifdef HAVE_GD_PNG
+ if (!strcmp(t->name, "png") || !strcmp(t->name, "gif") || !strcmp(t->name, "jpeg")) {
+ (*t->text_angle)(0);
+ str = "this terminal supports text rotation only for truetype fonts";
+ (*t->put_text)(t->v_char * 2 + t->h_char * 4, ymax_t / 2 - t->v_char * 2, str);
+ }
+#endif
+ } else {
+ (void) (*t->justify_text) (LEFT);
+ (*t->put_text) (t->h_char * 2, ymax_t / 2 - t->v_char * 2, "can't rotate text");
+ }
+ (void) (*t->justify_text) (LEFT);
+ (void) (*t->text_angle) (0);
+ (*t->linetype)(LT_BLACK);
+
+ /* test tic size */
+ (*t->linetype)(4);
+ (*t->move) ((unsigned int) (xmax_t / 2 + t->h_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)), (unsigned int) ymax_t - 1);
+ (*t->vector) ((unsigned int) (xmax_t / 2 + t->h_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)),
+ (unsigned int) (ymax_t - axis_array[FIRST_X_AXIS].ticscale * t->v_tic));
+ (*t->move) ((unsigned int) (xmax_t / 2), (unsigned int) (ymax_t - t->v_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)));
+ (*t->vector) ((unsigned int) (xmax_t / 2 + axis_array[FIRST_X_AXIS].ticscale * t->h_tic),
+ (unsigned int) (ymax_t - t->v_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)));
+ /* HBB 19990530: changed this to use right-justification, if possible... */
+ str = "show ticscale";
+ if ((*t->justify_text) (RIGHT))
+ (*t->put_text) ((unsigned int) (xmax_t / 2 - 1* t->h_char),
+ (unsigned int) (ymax_t - (t->v_tic * 2 + t->v_char / 2)),
+ str);
+ else
+ (*t->put_text) ((unsigned int) (xmax_t / 2 - (strlen(str)+1) * t->h_char),
+ (unsigned int) (ymax_t - (t->v_tic * 2 + t->v_char / 2)),
+ str);
+ (void) (*t->justify_text) (LEFT);
+ (*t->linetype)(LT_BLACK);
+
+ /* test line and point types */
+ x = xmax_t - t->h_char * 6 - p_width;
+ y = ymax_t - key_entry_height;
+ (*t->pointsize) (pointsize);
+ for (i = -2; y > key_entry_height; i++) {
+ (*t->linetype) (i);
+ /* (void) sprintf(label,"%d",i); Jorgen Lippert
+ lippert@risoe.dk */
+ (void) sprintf(label, "%d", i + 1);
+ if ((*t->justify_text) (RIGHT))
+ (*t->put_text) (x, y, label);
+ else
+ (*t->put_text) (x - strlen(label) * t->h_char, y, label);
+ (*t->move) (x + t->h_char, y);
+ (*t->vector) (x + t->h_char * 4, y);
+ if (i >= -1)
+ (*t->point) (x + t->h_char * 5 + p_width / 2, y, i);
+ y -= key_entry_height;
+ }
+
+ /* test some arrows */
+ (*t->linewidth) (1.0);
+ (*t->linetype) (0);
+ x = xmax_t * .375;
+ y = ymax_t * .250;
+ xl = t->h_tic * 7;
+ yl = t->v_tic * 7;
+ i = curr_arrow_headfilled;
+ curr_arrow_headfilled = 0;
+ (*t->arrow) (x, y, x + xl, y, END_HEAD);
+ curr_arrow_headfilled = 1;
+ (*t->arrow) (x, y, x - xl, y, END_HEAD);
+ curr_arrow_headfilled = 2;
+ (*t->arrow) (x, y, x, y + yl, END_HEAD);
+ curr_arrow_headfilled = 1; /* Was 3, but no terminals implement it */
+ (*t->arrow) (x, y, x, y - yl, END_HEAD);
+ curr_arrow_headfilled = i;
+ xl = t->h_tic * 5;
+ yl = t->v_tic * 5;
+ (*t->arrow) (x - xl, y - yl, x + xl, y + yl, END_HEAD | BACKHEAD);
+ (*t->arrow) (x - xl, y + yl, x, y, NOHEAD);
+ curr_arrow_headfilled = 1; /* Was 3, but no terminals implement it */
+ (*t->arrow) (x, y, x + xl, y - yl, BACKHEAD);
+
+ /* test line widths */
+ (void) (*t->justify_text) (LEFT);
+ xl = xmax_t / 10;
+ yl = ymax_t / 25;
+ x = xmax_t * .075;
+ y = yl;
+
+ for (i=1; i<7; i++) {
+ (*t->linewidth) ((float)(i)); (*t->linetype)(LT_BLACK);
+ (*t->move) (x, y); (*t->vector) (x+xl, y);
+ sprintf(label," lw %1d%c",i,0);
+ (*t->put_text) (x+xl, y, label);
+ y += yl;
+ }
+ (*t->put_text) (x, y, "linewidth");
+
+ /* test fill patterns */
+ x = xmax_t * 0.5;
+ y = 0;
+ xl = xmax_t / 40;
+ yl = ymax_t / 8;
+ (*t->linewidth) ((float)(1));
+ (*t->linetype)(LT_BLACK);
+ (*t->justify_text) (CENTRE);
+ (*t->put_text)(x+xl*7, yl+t->v_char*1.5, "pattern fill");
+ for (i=0; i<10; i++) {
+ int style = ((i<<4) + FS_PATTERN);
+ if (t->fillbox)
+ (*t->fillbox) ( style, x, y, xl, yl );
+ (*t->move) (x,y);
+ (*t->vector)(x,y+yl);
+ (*t->vector)(x+xl,y+yl);
+ (*t->vector)(x+xl,y);
+ (*t->vector)(x,y);
+ sprintf(label,"%2d",i);
+ (*t->put_text)(x+xl/2, y+yl+t->v_char*0.5, label);
+ x += xl * 1.5;
+ }
+
+ {
+ int cen_x = (int)(0.75 * xmax_t);
+ int cen_y = (int)(0.83 * ymax_t);
+ int radius = xmax_t / 20;
+
+ (*t->linetype)(2);
+ /* test pm3d -- filled_polygon(), but not set_color() */
+ if (t->filled_polygon) {
+#define NUMBER_OF_VERTICES 6
+ int n = NUMBER_OF_VERTICES;
+ gpiPoint corners[NUMBER_OF_VERTICES+1];
+#undef NUMBER_OF_VERTICES
+ int i;
+
+ for (i = 0; i < n; i++) {
+ corners[i].x = cen_x + radius * cos(2*M_PI*i/n);
+ corners[i].y = cen_y + radius * sin(2*M_PI*i/n);
+ }
+ corners[n].x = corners[0].x;
+ corners[n].y = corners[0].y;
+ corners->style = FS_OPAQUE;
+ term->filled_polygon(n+1, corners);
+ str = "(color) filled polygon:";
+ } else
+ str = "filled polygons not supported";
+ (*t->linetype)(LT_BLACK);
+ i = ((*t->justify_text) (CENTRE)) ? 0 : t->h_char * strlen(str) / 2;
+ (*t->put_text) (cen_x + i, cen_y + radius + t->v_char * 0.5, str);
+ (*t->linetype)(LT_BLACK);
+ }
+
+ term_end_plot();
+}
+
+#if 0
+# if defined(MSDOS)||defined(g)||defined(MTOS)||defined(OS2)||defined(_Windows)||defined(DOS386)
+
+/* output for some terminal types must be binary to stop non Unix computers
+ changing \n to \r\n.
+ If the output is not STDOUT, the following code reopens gpoutfile
+ with binary mode. */
+void
+reopen_binary()
+{
+ if (outstr) {
+ (void) fclose(gpoutfile);
+# ifdef _Windows
+ if (!stricmp(outstr, "PRN")) {
+ /* use temp file for windows */
+ (void) strcpy(filename, win_prntmp);
+ }
+# endif
+ if ((gpoutfile = fopen(filename, "wb")) == (FILE *) NULL) {
+ if ((gpoutfile = fopen(filename, "w")) == (FILE *) NULL) {
+ os_error(NO_CARET, "cannot reopen file with binary type; output unknown");
+ } else {
+ os_error(NO_CARET, "cannot reopen file with binary type; output reset to ascii");
+ }
+ }
+# if defined(__TURBOC__) && defined(MSDOS)
+# ifndef _Windows
+ if (!stricmp(outstr, "PRN")) {
+ /* Put the printer into binary mode. */
+ union REGS regs;
+ regs.h.ah = 0x44; /* ioctl */
+ regs.h.al = 0; /* get device info */
+ regs.x.bx = fileno(gpoutfile);
+ intdos(®s, ®s);
+ regs.h.dl |= 0x20; /* binary (no ^Z intervention) */
+ regs.h.dh = 0;
+ regs.h.ah = 0x44; /* ioctl */
+ regs.h.al = 1; /* set device info */
+ intdos(®s, ®s);
+ }
+# endif /* !_Windows */
+# endif /* TURBOC && MSDOS */
+ }
+}
+
+# endif /* MSDOS || g || MTOS || ... */
+#endif /* 0 */
+
+#ifdef VMS
+/* these are needed to modify terminal characteristics */
+# ifndef VWS_XMAX
+ /* avoid duplicate warning; VWS includes these */
+# include <descrip.h>
+# include <ssdef.h>
+# endif /* !VWS_MAX */
+# include <iodef.h>
+# include <ttdef.h>
+# include <tt2def.h>
+# include <dcdef.h>
+# include <stat.h>
+# include <fab.h>
+/* If you use WATCOM C or a very strict ANSI compiler, you may have to
+ * delete or comment out the following 3 lines: */
+# ifndef TT2$M_DECCRT3 /* VT300 not defined as of VAXC v2.4 */
+# define TT2$M_DECCRT3 0X80000000
+# endif
+static unsigned short chan;
+static int old_char_buf[3], cur_char_buf[3];
+$DESCRIPTOR(sysoutput_desc, "SYS$OUTPUT");
+
+/* Look first for decw$display (decterms do regis). Determine if we
+ * have a regis terminal and save terminal characteristics */
+char *
+vms_init()
+{
+ /* Save terminal characteristics in old_char_buf and
+ initialise cur_char_buf to current settings. */
+ int i;
+ if (getenv("DECW$DISPLAY"))
+ return ("x11");
+ atexit(vms_reset);
+ sys$assign(&sysoutput_desc, &chan, 0, 0);
+ sys$qiow(0, chan, IO$_SENSEMODE, 0, 0, 0, old_char_buf, 12, 0, 0, 0, 0);
+ for (i = 0; i < 3; ++i)
+ cur_char_buf[i] = old_char_buf[i];
+ sys$dassgn(chan);
+
+ /* Test if terminal is regis */
+ if ((cur_char_buf[2] & TT2$M_REGIS) == TT2$M_REGIS)
+ return ("regis");
+ return (NULL);
+}
+
+/* set terminal to original state */
+void
+vms_reset()
+{
+ int i;
+
+ sys$assign(&sysoutput_desc, &chan, 0, 0);
+ sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, old_char_buf, 12, 0, 0, 0, 0);
+ for (i = 0; i < 3; ++i)
+ cur_char_buf[i] = old_char_buf[i];
+ sys$dassgn(chan);
+}
+
+/* set terminal mode to tektronix */
+void
+term_mode_tek()
+{
+ long status;
+
+ if (gpoutfile != stdout)
+ return; /* don't modify if not stdout */
+ sys$assign(&sysoutput_desc, &chan, 0, 0);
+ cur_char_buf[0] = 0x004A0000 | DC$_TERM | (TT$_TEK401X << 8);
+ cur_char_buf[1] = (cur_char_buf[1] & 0x00FFFFFF) | 0x18000000;
+
+ cur_char_buf[1] &= ~TT$M_CRFILL;
+ cur_char_buf[1] &= ~TT$M_ESCAPE;
+ cur_char_buf[1] &= ~TT$M_HALFDUP;
+ cur_char_buf[1] &= ~TT$M_LFFILL;
+ cur_char_buf[1] &= ~TT$M_MECHFORM;
+ cur_char_buf[1] &= ~TT$M_NOBRDCST;
+ cur_char_buf[1] &= ~TT$M_NOECHO;
+ cur_char_buf[1] &= ~TT$M_READSYNC;
+ cur_char_buf[1] &= ~TT$M_REMOTE;
+ cur_char_buf[1] |= TT$M_LOWER;
+ cur_char_buf[1] |= TT$M_TTSYNC;
+ cur_char_buf[1] |= TT$M_WRAP;
+ cur_char_buf[1] &= ~TT$M_EIGHTBIT;
+ cur_char_buf[1] &= ~TT$M_MECHTAB;
+ cur_char_buf[1] &= ~TT$M_SCOPE;
+ cur_char_buf[1] |= TT$M_HOSTSYNC;
+
+ cur_char_buf[2] &= ~TT2$M_APP_KEYPAD;
+ cur_char_buf[2] &= ~TT2$M_BLOCK;
+ cur_char_buf[2] &= ~TT2$M_DECCRT3;
+ cur_char_buf[2] &= ~TT2$M_LOCALECHO;
+ cur_char_buf[2] &= ~TT2$M_PASTHRU;
+ cur_char_buf[2] &= ~TT2$M_REGIS;
+ cur_char_buf[2] &= ~TT2$M_SIXEL;
+ cur_char_buf[2] |= TT2$M_BRDCSTMBX;
+ cur_char_buf[2] |= TT2$M_EDITING;
+ cur_char_buf[2] |= TT2$M_INSERT;
+ cur_char_buf[2] |= TT2$M_PRINTER;
+ cur_char_buf[2] &= ~TT2$M_ANSICRT;
+ cur_char_buf[2] &= ~TT2$M_AVO;
+ cur_char_buf[2] &= ~TT2$M_DECCRT;
+ cur_char_buf[2] &= ~TT2$M_DECCRT2;
+ cur_char_buf[2] &= ~TT2$M_DRCS;
+ cur_char_buf[2] &= ~TT2$M_EDIT;
+ cur_char_buf[2] |= TT2$M_FALLBACK;
+
+ status = sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, cur_char_buf, 12, 0, 0, 0, 0);
+ if (status == SS$_BADPARAM) {
+ /* terminal fallback utility not installed on system */
+ cur_char_buf[2] &= ~TT2$M_FALLBACK;
+ sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, cur_char_buf, 12, 0, 0, 0, 0);
+ } else {
+ if (status != SS$_NORMAL)
+ lib$signal(status, 0, 0);
+ }
+ sys$dassgn(chan);
+}
+
+/* set terminal mode back to native */
+void
+term_mode_native()
+{
+ int i;
+
+ if (gpoutfile != stdout)
+ return; /* don't modify if not stdout */
+ sys$assign(&sysoutput_desc, &chan, 0, 0);
+ sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, old_char_buf, 12, 0, 0, 0, 0);
+ for (i = 0; i < 3; ++i)
+ cur_char_buf[i] = old_char_buf[i];
+ sys$dassgn(chan);
+}
+
+/* set terminal mode pasthru */
+void
+term_pasthru()
+{
+ if (gpoutfile != stdout)
+ return; /* don't modify if not stdout */
+ sys$assign(&sysoutput_desc, &chan, 0, 0);
+ cur_char_buf[2] |= TT2$M_PASTHRU;
+ sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, cur_char_buf, 12, 0, 0, 0, 0);
+ sys$dassgn(chan);
+}
+
+/* set terminal mode nopasthru */
+void
+term_nopasthru()
+{
+ if (gpoutfile != stdout)
+ return; /* don't modify if not stdout */
+ sys$assign(&sysoutput_desc, &chan, 0, 0);
+ cur_char_buf[2] &= ~TT2$M_PASTHRU;
+ sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, cur_char_buf, 12, 0, 0, 0, 0);
+ sys$dassgn(chan);
+}
+
+void
+fflush_binary()
+{
+ typedef short int INT16; /* signed 16-bit integers */
+ INT16 k; /* loop index */
+
+ if (gpoutfile != stdout) {
+ /* Stupid VMS fflush() raises error and loses last data block
+ unless it is full for a fixed-length record binary file.
+ Pad it here with NULL characters. */
+ for (k = (INT16) ((*gpoutfile)->_cnt); k > 0; --k)
+ putc('\0', gpoutfile);
+ fflush(gpoutfile);
+ }
+}
+#endif /* VMS */
+
+/*
+ * This is an abstraction of the enhanced text mode originally written
+ * for the postscript terminal driver by David Denholm and Matt Heffron.
+ * I have split out a terminal-independent recursive syntax-parser
+ * routine that can be shared by all drivers that want to add support
+ * for enhanced text mode.
+ *
+ * A driver that wants to make use of this common framework must provide
+ * three new entries in TERM_TABLE:
+ * void *enhanced_open (char *fontname, double fontsize, double base,
+ * TBOOLEAN widthflag, TBOOLEAN showflag,
+ * int overprint)
+ * void *enhanced_writec (char c)
+ * void *enhanced_flush ()
+ *
+ * Each driver also has a separate ENHXX_put_text() routine that replaces
+ * the normal (term->put_text) routine while in enhanced mode.
+ * This routine must initialize the following globals used by the shared code:
+ * enhanced_fontscale converts font size to device resolution units
+ * enhanced_escape_format used to process octal escape characters \xyz
+ *
+ * I bent over backwards to make the output of the revised code identical
+ * to the output of the original postscript version. That means there is
+ * some cruft left in here (enhanced_max_height for one thing, and all
+ * the code relating to RememberFont) that is probably irrelevant to any
+ * new drivers using the code.
+ *
+ * Ethan A Merritt - November 2003
+ */
+
+#ifdef DEBUG_ENH
+#define ENH_DEBUG(x) printf x;
+#else
+#define ENH_DEBUG(x)
+#endif
+
+void
+do_enh_writec(int c)
+{
+ /* note: c is meant to hold a char, but is actually an int, for
+ * the same reasons applying to putc() and friends */
+ *enhanced_cur_text++ = c;
+}
+
+/*
+ * Process a bit of string, and return the last character used.
+ * p is start of string
+ * brace is TRUE to keep processing to }, FALSE to do one character only
+ * fontname & fontsize are obvious
+ * base is the current baseline
+ * widthflag is TRUE if the width of this should count,
+ * FALSE for zero width boxes
+ * showflag is TRUE if this should be shown,
+ * FALSE if it should not be shown (like TeX \phantom)
+ * overprint is 0 for normal operation,
+ * 1 for the underprinted text (included in width calculation),
+ * 2 for the overprinted text (not included in width calc)
+ * (overprinted text is centered horizontally on underprinted text
+ */
+
+char *
+enhanced_recursion(
+ char *p,
+ TBOOLEAN brace,
+ char *fontname,
+ double fontsize,
+ double base,
+ TBOOLEAN widthflag,
+ TBOOLEAN showflag,
+ int overprint)
+{
+ ENH_DEBUG(("RECURSE WITH [%p] \"%s\", %d %s %.1f %.1f %d %d %d\n", p, p, brace, fontname, fontsize, base, widthflag, showflag, overprint));
+
+ /* Start each recursion with a clean string */
+ (term->enhanced_flush)();
+
+ if (base + fontsize > enhanced_max_height) {
+ enhanced_max_height = base + fontsize;
+ ENH_DEBUG(("Setting max height to %.1f\n", enhanced_max_height));
+ }
+
+ if (base < enhanced_min_height) {
+ enhanced_min_height = base;
+ ENH_DEBUG(("Setting min height to %.1f\n", enhanced_min_height));
+ }
+
+ while (*p) {
+ float shift;
+
+ switch (*p) {
+ case '}' :
+ /*{{{ deal with it*/
+ if (brace)
+ return (p);
+
+ fputs("enhanced text parser - spurious }\n", stderr);
+ break;
+ /*}}}*/
+
+ case '_' :
+ case '^' :
+ /*{{{ deal with super/sub script*/
+ shift = (*p == '^') ? 0.5 : -0.3;
+ (term->enhanced_flush)();
+ p = enhanced_recursion(p + 1, FALSE, fontname, fontsize * 0.8,
+ base + shift * fontsize, widthflag,
+ showflag, overprint);
+ break;
+ /*}}}*/
+ case '{' :
+ {
+ char *savepos = NULL, save = 0;
+ char *localfontname = fontname, ch;
+ int recode = 1;
+ float f = fontsize, ovp;
+
+ /*{{{ recurse (possibly with a new font) */
+
+ ENH_DEBUG(("Dealing with {\n"));
+
+ /* get vertical offset (if present) for overprinted text */
+ while (*++p == ' ');
+ if (overprint == 2) {
+ ovp = (float)strtod(p,&p);
+ if (term->flags & TERM_IS_POSTSCRIPT)
+ base = ovp*f;
+ else
+ base += ovp*f;
+ }
+ --p; /* HBB 20001021: bug fix: 10^{2} broken */
+
+ if (*++p == '/') {
+ /* then parse a fontname, optional fontsize */
+ while (*++p == ' ')
+ ; /* do nothing */
+ if (*p=='-') {
+ recode = 0;
+ while (*++p == ' ')
+ ; /* do nothing */
+ }
+ localfontname = p;
+ while ((ch = *p) > ' ' && ch != '=' && ch != '*' && ch != '}')
+ ++p;
+ save = *(savepos=p);
+ if (ch == '=') {
+ *p++ = '\0';
+ /*{{{ get optional font size*/
+ ENH_DEBUG(("Calling strtod(\"%s\") ...", p));
+ f = (float)strtod(p, &p);
+ ENH_DEBUG(("Returned %.1f and \"%s\"\n", f, p));
+
+ if (f == 0)
+ f = fontsize;
+ else
+ f *= enhanced_fontscale; /* remember the scaling */
+
+ ENH_DEBUG(("Font size %.1f\n", f));
+ /*}}}*/
+ } else if (ch == '*') {
+ *p++ = '\0';
+ /*{{{ get optional font size scale factor*/
+ ENH_DEBUG(("Calling strtod(\"%s\") ...", p));
+ f = (float)strtod(p, &p);
+ ENH_DEBUG(("Returned %.1f and \"%s\"\n", f, p));
+
+ if (f)
+ f *= fontsize; /* apply the scale factor */
+ else
+ f = fontsize;
+
+ ENH_DEBUG(("Font size %.1f\n", f));
+ /*}}}*/
+ } else {
+ *p++ = '\0';
+ f = fontsize;
+ }
+
+ while (*p == ' ')
+ ++p;
+ if (! *localfontname) {
+ localfontname = fontname;
+#ifdef POSTSCRIPT_DRIVER
+ } else if (!strncmp("postscript",term->name,9)) {
+ /* FIXME - This cruft is left over from when the code */
+ /* was part of post.trm. No one else needs it! */
+ char *recodestring = (PS_RememberFont)(localfontname,
+ recode && !ENHps_opened_string);
+ if (recode && recodestring) {
+ (term->enhanced_flush)();
+ fprintf(gpoutfile, "/%s %s",
+ localfontname, recodestring);
+ }
+#endif
+ }
+ }
+ /*}}}*/
+
+ ENH_DEBUG(("Before recursing, we are at [%p] \"%s\"\n", p, p));
+
+ p = enhanced_recursion(p, TRUE, localfontname, f, base,
+ widthflag, showflag, overprint);
+
+ ENH_DEBUG(("BACK WITH \"%s\"\n", p));
+
+ (term->enhanced_flush)();
+
+ if (savepos)
+ /* restore overwritten character */
+ *savepos = save;
+ break;
+ } /* case '{' */
+ case '@' :
+ /*{{{ phantom box - prints next 'char', then restores currentpoint */
+ (term->enhanced_flush)();
+ (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, 3);
+ p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
+ widthflag, showflag, overprint);
+ (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, 4);
+ break;
+ /*}}}*/
+
+ case '&' :
+ /*{{{ character skip - skips space equal to length of character(s) */
+ (term->enhanced_flush)();
+
+ p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
+ widthflag, FALSE, overprint);
+ break;
+ /*}}}*/
+
+ case '~' :
+ /*{{{ overprinted text */
+ /* the second string is overwritten on the first, centered
+ * horizontally on the first and (optionally) vertically
+ * shifted by an amount specified (as a fraction of the
+ * current fontsize) at the beginning of the second string
+
+ * Note that in this implementation neither the under- nor
+ * overprinted string can contain syntax that would result
+ * in additional recursions -- no subscripts,
+ * superscripts, or anything else, with the exception of a
+ * font definition at the beginning of the text */
+
+ (term->enhanced_flush)();
+ p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
+ widthflag, showflag, 1);
+ (term->enhanced_flush)();
+ p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
+ FALSE, showflag, 2);
+
+ overprint = 0; /* may not be necessary, but just in case . . . */
+ break;
+ /*}}}*/
+
+ case '(' :
+ case ')' :
+ /*{{{ an escape and print it */
+ /* special cases */
+ (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
+ if (term->flags & TERM_IS_POSTSCRIPT)
+ (term->enhanced_writec)('\\');
+ (term->enhanced_writec)(*p);
+ break;
+ /*}}}*/
+
+ case '\\' :
+ if (p[1]=='\\' || p[1]=='(' || p[1]==')') {
+ (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
+ (term->enhanced_writec)('\\');
+
+ /*{{{ The enhanced mode always uses \xyz as an octal character representation
+ but each terminal type must give us the actual output format wanted.
+ pdf.trm wants the raw character code, which is why we use strtol();
+ most other terminal types want some variant of "\\%o". */
+ } else if (p[1] >= '0' && p[1] <= '7') {
+ char *e, escape[16], octal[4] = {'\0','\0','\0','\0'};
+
+ (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
+ octal[0] = *(++p);
+ if (p[1] >= '0' && p[1] <= '7') {
+ octal[1] = *(++p);
+ if (p[1] >= '0' && p[1] <= '7')
+ octal[2] = *(++p);
+ }
+ sprintf(escape, enhanced_escape_format, strtol(octal,NULL,8));
+ for (e=escape; *e; e++) {
+ (term->enhanced_writec)(*e);
+ }
+ break;
+ } else if (term->flags & TERM_IS_POSTSCRIPT) {
+ /* Shigeharu TAKENO Aug 2004 - Needed in order for shift-JIS */
+ /* encoding to work. If this change causes problems then we */
+ /* need a separate flag for shift-JIS and certain other 8-bit */
+ /* character sets. */
+ /* EAM Nov 2004 - Nevertheless we must allow \ to act as an */
+ /* escape for a few enhanced mode formatting characters even */
+ /* though it corrupts certain Shift-JIS character sequences. */
+ if (strchr("^_@&~{}",p[1]) == NULL) {
+ (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
+ (term->enhanced_writec)('\\');
+ (term->enhanced_writec)('\\');
+ break;
+ }
+ }
+ ++p;
+
+ /* HBB 20030122: Avoid broken output if there's a \
+ * exactly at the end of the line */
+ if (*p == '\0') {
+ fputs("enhanced text parser -- spurious backslash\n", stderr);
+ break;
+ }
+
+ /* just go and print it (fall into the 'default' case) */
+ /*}}}*/
+ default:
+ /*{{{ print it */
+ (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
+ (term->enhanced_writec)(*p);
+ /*}}}*/
+ } /* switch (*p) */
+
+ /* like TeX, we only do one character in a recursion, unless it's
+ * in braces
+ */
+
+ if (!brace) {
+ (term->enhanced_flush)();
+ return(p); /* the ++p in the outer copy will increment us */
+ }
+
+ if (*p) /* only not true if { not terminated, I think */
+ ++p;
+ } /* while (*p) */
+
+ (term->enhanced_flush)();
+ return p;
+}
+
+/* Called after the end of recursion to check for errors */
+void
+enh_err_check(const char *str)
+{
+ if (*str == '}')
+ fputs("enhanced text mode parser - ignoring spurious }\n", stderr);
+ else
+ fprintf(stderr, "enhanced text mode parsing error - *str=0x%x\n",
+ *str);
+}
+
+/* Helper function for multiplot auto layout to issue size and offest cmds */
+static void
+mp_layout_size_and_offset(void)
+{
+ if (!mp_layout.auto_layout) return;
+
+ /* fprintf(stderr,"col==%d row==%d\n",mp_layout.act_col,mp_layout.act_row); */
+ /* the 'set size' command */
+ xsize = mp_layout.xscale / mp_layout.num_cols;
+ ysize = mp_layout.yscale / mp_layout.num_rows;
+
+ /* the 'set origin' command */
+ xoffset = (double)(mp_layout.act_col) / mp_layout.num_cols;
+ if (mp_layout.downwards)
+ yoffset = 1.0 - (double)(mp_layout.act_row+1) / mp_layout.num_rows;
+ else
+ yoffset = (double)(mp_layout.act_row) / mp_layout.num_rows;
+ /* fprintf(stderr,"xoffset==%g yoffset==%g\n", xoffset,yoffset); */
+
+ /* Allow a little space at the top for a title */
+ if (mp_layout.title.text) {
+ ysize *= (1.0 - mp_layout.title_height);
+ yoffset *= (1.0 - mp_layout.title_height);
+ }
+
+ /* corrected for x/y-scaling factors and user defined offsets */
+ xoffset -= (mp_layout.xscale-1)/(2*mp_layout.num_cols);
+ yoffset -= (mp_layout.yscale-1)/(2*mp_layout.num_rows);
+ /* fprintf(stderr," xoffset==%g yoffset==%g\n", xoffset,yoffset); */
+ xoffset += mp_layout.xoffset;
+ yoffset += mp_layout.yoffset;
+ /* fprintf(stderr," xoffset==%g yoffset==%g\n", xoffset,yoffset); */
+}
+
+/*
+ * Text strings containing control information for enhanced text mode
+ * contain more characters than will actually appear in the output.
+ * This makes it hard to estimate how much horizontal space on the plot
+ * (e.g. in the key box) must be reserved to hold them. To approximate
+ * the eventually length we switch briefly to the dummy terminal driver
+ * "estimate.trm" and then switch back to the current terminal.
+ * If better, perhaps terminal-specific methods of estimation are
+ * developed later they can be slotted into this one call site.
+ */
+int
+estimate_strlen(char *text)
+{
+int len;
+
+#ifdef GP_ENH_EST
+ if (term->flags & TERM_ENHANCED_TEXT) {
+ struct termentry *tsave = term;
+ term = &ENHest;
+ term->put_text(0,0,text);
+ len = term->xmax;
+ term = tsave;
+ FPRINTF((stderr,"Estimating length %d for enhanced text string \"%s\"\n",len,text));
+ } else
+#endif
+ len = strlen(text);
+
+ return len;
+}
+
+void
+ignore_enhanced(TBOOLEAN flag)
+{
+ /* Force a return to the default font */
+ if (flag && !ignore_enhanced_text) {
+ ignore_enhanced_text = TRUE;
+ if (term->set_font)
+ term->set_font("");
+ }
+ ignore_enhanced_text = flag;
+}
+
+/* Simple-minded test for whether the point (x,y) is in bounds for the current terminal.
+ * Some terminals can do their own clipping, and can clip partial objects.
+ * If the flag TERM_CAN_CLIP is set, we skip this relative crude test and let the
+ * driver or the hardware handle clipping.
+ */
+TBOOLEAN
+on_page(int x, int y)
+{
+ if (term->flags & TERM_CAN_CLIP)
+ return TRUE;
+
+ if ((0 < x && x < term->xmax) && (0 < y && y < term->ymax))
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Utility routine for drivers to accept an explicit size for the
+ * output image.
+ */
+size_units
+parse_term_size( float *xsize, float *ysize, size_units default_units )
+{
+ size_units units = default_units;
+ struct value a;
+
+ if (END_OF_COMMAND)
+ int_error(c_token, "size requires two numbers: xsize, ysize");
+ *xsize = real(const_express(&a));
+ if (almost_equals(c_token,"in$ches")) {
+ c_token++;
+ units = INCHES;
+ } else if (equals(c_token,"cm")) {
+ c_token++;
+ units = CM;
+ }
+ switch (units) {
+ case INCHES: *xsize *= gp_resolution; break;
+ case CM: *xsize *= (float)gp_resolution / 2.54; break;
+ case PIXELS:
+ default: break;
+ }
+
+ if (!equals(c_token++,","))
+ int_error(c_token, "size requires two numbers: xsize, ysize");
+ *ysize = real(const_express(&a));
+ if (almost_equals(c_token,"in$ches")) {
+ c_token++;
+ units = INCHES;
+ } else if (equals(c_token,"cm")) {
+ c_token++;
+ units = CM;
+ }
+ switch (units) {
+ case INCHES: *ysize *= gp_resolution; break;
+ case CM: *ysize *= (float)gp_resolution / 2.54; break;
+ case PIXELS:
+ default: break;
+ }
+
+ if (*xsize < 1 || *ysize < 1)
+ int_error(c_token, "size: out of range");
+
+ return units;
+}
+
+/*
+ * Wrappers for newpath and closepath
+ */
+
+void
+newpath()
+{
+ if (term->path)
+ (*term->path)(0);
+}
+
+void
+closepath()
+{
+ if (term->path)
+ (*term->path)(1);
+}