1 /* GNUPLOT - term.c */
2 
3 /*[
4  * Copyright 1986 - 1993, 1998, 2004   Thomas Williams, Colin Kelley
5  *
6  * Permission to use, copy, and distribute this software and its
7  * documentation for any purpose with or without fee is hereby granted,
8  * provided that the above copyright notice appear in all copies and
9  * that both that copyright notice and this permission notice appear
10  * in supporting documentation.
11  *
12  * Permission to modify the software is granted, but not the right to
13  * distribute the complete modified source code.  Modifications are to
14  * be distributed as patches to the released version.  Permission to
15  * distribute binaries produced by compiling modified sources is granted,
16  * provided you
17  *   1. distribute the corresponding source modifications from the
18  *    released version in the form of a patch file along with the binaries,
19  *   2. add special version identification to distinguish your version
20  *    in addition to the base release version number,
21  *   3. provide your name and address as the primary contact for the
22  *    support of your modified version, and
23  *   4. retain our contact information in regard to use of the base
24  *    software.
25  * Permission to distribute the released version of the source code along
26  * with corresponding source modifications in the form of a patch file is
27  * granted with same provisions 2 through 4 for binary distributions.
28  *
29  * This software is provided "as is" without express or implied warranty
30  * to the extent permitted by applicable law.
31 ]*/
32 
33 
34  /* This module is responsible for looking after the terminal
35   * drivers at the lowest level. Only this module (should)
36   * know about all the various rules about interpreting
37   * the terminal capabilities exported by the terminal
38   * drivers in the table.
39   *
40   * Note that, as far as this module is concerned, a
41   * terminal session lasts only until _either_ terminal
42   * or output file changes. Before either is changed,
43   * the terminal is shut down.
44   *
45   * Entry points : (see also term/README)
46   *
47   * term_set_output() : called when  set output  invoked
48   *
49   * term_initialise()  : optional. Prepare the terminal for first
50   *                use. It protects itself against subsequent calls.
51   *
52   * term_start_plot() : called at start of graph output. Calls term_init
53   *                     if necessary
54   *
55   * term_apply_lp_properties() : apply linewidth settings
56   *
57   * term_end_plot() : called at the end of a plot
58   *
59   * term_reset() : called during int_error handling, to shut
60   *                terminal down cleanly
61   *
62   * term_start_multiplot() : called by   set multiplot
63   *
64   * term_end_multiplot() : called by  set nomultiplot
65   *
66   * term_check_multiplot_okay() : called just before an interactive
67   *                        prompt is issued while in multiplot mode,
68   *                        to allow terminal to suspend if necessary,
69   *                        Raises an error if interactive multiplot
70   *                       is not supported.
71   */
72 
73 #include "term_api.h"
74 
75 #include "alloc.h"
76 #include "axis.h"
77 #ifndef NO_BITMAP_SUPPORT
78 #include "bitmap.h"
79 #endif
80 #include "command.h"
81 #include "driver.h"
82 #include "graphics.h"
83 #include "help.h"
84 #include "plot.h"
85 #include "tables.h"
86 #include "getcolor.h"
87 #include "term.h"
88 #include "util.h"
89 #include "version.h"
90 #include "misc.h"
91 #include "multiplot.h"
92 #include "readline.h"
93 
94 #ifdef USE_MOUSE
95 #include "mouse.h"
96 #else
97 /* Some terminals (svg canvas) can provide mousing information */
98 /* even if the interactive gnuplot session itself cannot.      */
99 long mouse_mode = 0;
100 char* mouse_alt_string = NULL;
101 #endif
102 
103 #ifdef _WIN32
104 # include "win/winmain.h"
105 # include "win/wcommon.h"
106 # ifdef __MSC__
107 #  include <malloc.h>
108 #  include <io.h>
109 # else
110 #  include <alloc.h>
111 # endif                         /* MSC */
112 #endif /* _WIN32 */
113 
114 static int termcomp __PROTO((const generic * a, const generic * b));
115 
116 /* Externally visible variables */
117 /* the central instance: the current terminal's interface structure */
118 struct termentry *term = NULL;  /* unknown */
119 
120 /* ... and its options string */
121 char term_options[MAX_LINE_LEN+1] = "";
122 
123 /* the 'output' file name and handle */
124 char *outstr = NULL;            /* means "STDOUT" */
125 FILE *gpoutfile;
126 
127 /* Output file where the PostScript output goes to. See term_api.h for more
128    details.
129 */
130 FILE *gppsfile = 0;
131 char *PS_psdir = NULL;
132 
133 /* true if terminal has been initialized */
134 TBOOLEAN term_initialised;
135 
136 /* The qt and wxt terminals cannot be used in the same session. */
137 /* Whichever one is used first to plot, this locks out the other. */
138 void *term_interlock = NULL;
139 
140 /* true if "set monochrome" */
141 TBOOLEAN monochrome = FALSE;
142 
143 /* true if in multiplot mode */
144 TBOOLEAN multiplot = FALSE;
145 
146 /* text output encoding, for terminals that support it */
147 enum set_encoding_id encoding;
148 /* table of encoding names, for output of the setting */
149 const char *encoding_names[] = {
150     "default", "iso_8859_1", "iso_8859_2", "iso_8859_9", "iso_8859_15",
151     "cp437", "cp850", "cp852", "cp950", "cp1250", "cp1251", "cp1252", "cp1254",
152     "koi8r", "koi8u", "sjis", "utf8", NULL };
153 /* 'set encoding' options */
154 const struct gen_table set_encoding_tbl[] =
155 {
156     { "def$ault", S_ENC_DEFAULT },
157     { "utf$8", S_ENC_UTF8 },
158     { "iso$_8859_1", S_ENC_ISO8859_1 },
159     { "iso_8859_2", S_ENC_ISO8859_2 },
160     { "iso_8859_9", S_ENC_ISO8859_9 },
161     { "iso_8859_15", S_ENC_ISO8859_15 },
162     { "cp4$37", S_ENC_CP437 },
163     { "cp850", S_ENC_CP850 },
164     { "cp852", S_ENC_CP852 },
165     { "cp950", S_ENC_CP950 },
166     { "cp1250", S_ENC_CP1250 },
167     { "cp1251", S_ENC_CP1251 },
168     { "cp1252", S_ENC_CP1252 },
169     { "cp1254", S_ENC_CP1254 },
170     { "koi8$r", S_ENC_KOI8_R },
171     { "koi8$u", S_ENC_KOI8_U },
172     { "sj$is", S_ENC_SJIS },
173     { NULL, S_ENC_INVALID }
174 };
175 
176 const char *arrow_head_names[4] =
177     {"nohead", "head", "backhead", "heads"};
178 
179 enum { IPC_BACK_UNUSABLE = -2, IPC_BACK_CLOSED = -1 };
180 #ifdef PIPE_IPC
181 /* HBB 20020225: currently not used anywhere outside term.c */
182 static SELECT_TYPE_ARG1 ipc_back_fd = IPC_BACK_CLOSED;
183 #endif
184 
185 /* resolution in dpi for converting pixels to size units */
186 int gp_resolution = 72;
187 
188 /* Support for enhanced text mode. Declared extern in term_api.h */
189 char  enhanced_text[MAX_LINE_LEN+1] = "";
190 char *enhanced_cur_text = NULL;
191 double enhanced_fontscale = 1.0;
192 char enhanced_escape_format[16] = "";
193 double enhanced_max_height = 0.0, enhanced_min_height = 0.0;
194 /* flag variable to disable enhanced output of filenames, mainly. */
195 TBOOLEAN ignore_enhanced_text = FALSE;
196 
197 /* Recycle count for user-defined linetypes */
198 int linetype_recycle_count = 0;
199 int mono_recycle_count = 0;
200 
201 
202 /* Internal variables */
203 
204 /* true if terminal is in graphics mode */
205 static TBOOLEAN term_graphics = FALSE;
206 
207 /* we have suspended the driver, in multiplot mode */
208 static TBOOLEAN term_suspended = FALSE;
209 
210 /* true if? */
211 static TBOOLEAN opened_binary = FALSE;
212 
213 /* true if require terminal to be initialized */
214 static TBOOLEAN term_force_init = FALSE;
215 
216 /* internal pointsize for do_point */
217 static double term_pointsize=1;
218 
219 /* Internal prototypes: */
220 
221 static void term_suspend __PROTO((void));
222 static void term_close_output __PROTO((void));
223 
224 static void null_linewidth __PROTO((double));
225 static void do_point __PROTO((unsigned int x, unsigned int y, int number));
226 static void do_pointsize __PROTO((double size));
227 static void line_and_point __PROTO((unsigned int x, unsigned int y, int number));
228 static void do_arrow __PROTO((unsigned int sx, unsigned int sy, unsigned int ex, unsigned int ey, int head));
229 static void null_dashtype __PROTO((int type, t_dashtype *custom_dash_pattern));
230 
231 static int null_text_angle __PROTO((int ang));
232 static int null_justify_text __PROTO((enum JUSTIFY just));
233 static int null_scale __PROTO((double x, double y));
234 static void null_layer __PROTO((t_termlayer layer));
235 static int null_set_font __PROTO((const char *font));
236 static void null_set_color __PROTO((struct t_colorspec *colorspec));
237 static void options_null __PROTO((void));
238 static void graphics_null __PROTO((void));
239 static void UNKNOWN_null __PROTO((void));
240 static void MOVE_null __PROTO((unsigned int, unsigned int));
241 static void LINETYPE_null __PROTO((int));
242 static void PUTTEXT_null __PROTO((unsigned int, unsigned int, const char *));
243 
244 static int strlen_tex __PROTO((const char *));
245 
246 static char *stylefont __PROTO((const char *fontname, TBOOLEAN isbold, TBOOLEAN isitalic));
247 
248 /* Used by terminals and by shared routine parse_term_size() */
249 typedef enum {
250     PIXELS,
251     INCHES,
252     CM
253 } size_units;
254 static size_units parse_term_size __PROTO((float *xsize, float *ysize, size_units def_units));
255 
256 
257 #ifdef VMS
258 char *vms_init();
259 void vms_reset();
260 void term_mode_tek();
261 void term_mode_native();
262 void term_pasthru();
263 void term_nopasthru();
264 void fflush_binary();
265 # define FOPEN_BINARY(file) fopen(file, "wb", "rfm=fix", "bls=512", "mrs=512")
266 #else /* !VMS */
267 # define FOPEN_BINARY(file) fopen(file, "wb")
268 #endif /* !VMS */
269 
270 #if defined(MSDOS) || defined(_WIN32)
271 # if defined(__DJGPP__)
272 #  include <io.h>
273 # endif
274 # include <fcntl.h>
275 # ifndef O_BINARY
276 #  ifdef _O_BINARY
277 #   define O_BINARY _O_BINARY
278 #  else
279 #   define O_BINARY O_BINARY_is_not_defined
280 #  endif
281 # endif
282 #endif
283 
284 #ifdef __EMX__
285 #include <io.h>
286 #include <fcntl.h>
287 #endif
288 
289 #if defined(__WATCOMC__) || defined(__MSC__)
290 # include <io.h>	/* for setmode() */
291 #endif
292 
293 #define NICE_LINE               0
294 #define POINT_TYPES             6
295 
296 #ifndef DEFAULTTERM
297 # define DEFAULTTERM NULL
298 #endif
299 
300 /* interface to the rest of gnuplot - the rules are getting
301  * too complex for the rest of gnuplot to be allowed in
302  */
303 
304 #if defined(PIPES)
305 static TBOOLEAN output_pipe_open = FALSE;
306 #endif /* PIPES */
307 
308 static void
term_close_output()309 term_close_output()
310 {
311     FPRINTF((stderr, "term_close_output\n"));
312 
313     opened_binary = FALSE;
314 
315     if (!outstr)                /* ie using stdout */
316 	return;
317 
318 #if defined(PIPES)
319     if (output_pipe_open) {
320 	(void) pclose(gpoutfile);
321 	output_pipe_open = FALSE;
322     } else
323 #endif /* PIPES */
324 #ifdef _WIN32
325     if (stricmp(outstr, "PRN") == 0)
326 	close_printer(gpoutfile);
327     else
328 #endif
329     if (gpoutfile != gppsfile)
330 	fclose(gpoutfile);
331 
332     gpoutfile = stdout;         /* Don't dup... */
333     free(outstr);
334     outstr = NULL;
335 
336     if (gppsfile)
337 	fclose(gppsfile);
338     gppsfile = NULL;
339 }
340 
341 /* assigns dest to outstr, so it must be allocated or NULL
342  * and it must not be outstr itself !
343  */
344 void
term_set_output(char * dest)345 term_set_output(char *dest)
346 {
347     FILE *f = NULL;
348 
349     FPRINTF((stderr, "term_set_output\n"));
350     assert(dest == NULL || dest != outstr);
351 
352     if (multiplot) {
353 	fputs("In multiplot mode you can't change the output\n", stderr);
354 	return;
355     }
356     if (term && term_initialised) {
357 	(*term->reset) ();
358 	term_initialised = FALSE;
359 	/* switch off output to special postscript file (if used) */
360 	gppsfile = NULL;
361     }
362     if (dest == NULL) {         /* stdout */
363 	term_close_output();
364     } else {
365 
366 #if defined(PIPES)
367 	if (*dest == '|') {
368 	    restrict_popen();
369 #ifdef _WIN32
370 	    if (term && (term->flags & TERM_BINARY))
371 		f = popen(dest + 1, "wb");
372 	    else
373 		f = popen(dest + 1, "w");
374 #else
375 	    f = popen(dest + 1, "w");
376 #endif
377 	    if (f == (FILE *) NULL)
378 		os_error(c_token, "cannot create pipe; output not changed");
379 	    else
380 		output_pipe_open = TRUE;
381 	} else {
382 #endif /* PIPES */
383 
384 #ifdef _WIN32
385 	if (outstr && stricmp(outstr, "PRN") == 0) {
386 	    /* we can't call open_printer() while printer is open, so */
387 	    close_printer(gpoutfile);   /* close printer immediately if open */
388 	    gpoutfile = stdout; /* and reset output to stdout */
389 	    free(outstr);
390 	    outstr = NULL;
391 	}
392 	if (stricmp(dest, "PRN") == 0) {
393 	    if ((f = open_printer()) == (FILE *) NULL)
394 		os_error(c_token, "cannot open printer temporary file; output may have changed");
395 	} else
396 #endif
397 
398 	{
399 #if defined (MSDOS)
400 	    if (outstr && (0 == stricmp(outstr, dest))) {
401 		/* On MSDOS, you cannot open the same file twice and
402 		 * then close the first-opened one and keep the second
403 		 * open, it seems. If you do, you get lost clusters
404 		 * (connection to the first version of the file is
405 		 * lost, it seems). */
406 		/* FIXME: this is not yet safe enough. You can fool it by
407 		 * specifying the same output file in two different ways
408 		 * (relative vs. absolute path to file, e.g.) */
409 		term_close_output();
410 	    }
411 #endif
412 	    if (term && (term->flags & TERM_BINARY))
413 		f = FOPEN_BINARY(dest);
414 	    else
415 		f = fopen(dest, "w");
416 
417 	    if (f == (FILE *) NULL)
418 		os_error(c_token, "cannot open file; output not changed");
419 	}
420 #if defined(PIPES)
421 	}
422 #endif
423 
424 	term_close_output();
425 	gpoutfile = f;
426 	outstr = dest;
427 	opened_binary = (term && (term->flags & TERM_BINARY));
428     }
429 }
430 
431 void
term_initialise()432 term_initialise()
433 {
434     FPRINTF((stderr, "term_initialise()\n"));
435 
436     if (!term)
437 	int_error(NO_CARET, "No terminal defined");
438 
439     /* check if we have opened the output file in the wrong mode
440      * (text/binary), if set term comes after set output
441      * This was originally done in change_term, but that
442      * resulted in output files being truncated
443      */
444 
445     if (outstr && (term->flags & TERM_NO_OUTPUTFILE)) {
446 	if (interactive)
447 	    fprintf(stderr,"Closing %s\n",outstr);
448 	term_close_output();
449     }
450 
451     if (outstr &&
452 	(((term->flags & TERM_BINARY) && !opened_binary) ||
453 	 ((!(term->flags & TERM_BINARY) && opened_binary)))) {
454 	/* this is nasty - we cannot just term_set_output(outstr)
455 	 * since term_set_output will first free outstr and we
456 	 * end up with an invalid pointer. I think I would
457 	 * prefer to defer opening output file until first plot.
458 	 */
459 	char *temp = gp_alloc(strlen(outstr) + 1, "temp file string");
460 	if (temp) {
461 	    FPRINTF((stderr, "term_initialise: reopening \"%s\" as %s\n",
462 		     outstr, term->flags & TERM_BINARY ? "binary" : "text"));
463 	    strcpy(temp, outstr);
464 	    term_set_output(temp);      /* will free outstr */
465 	    if (temp != outstr) {
466 		if (temp)
467 		    free(temp);
468 		temp = outstr;
469 	    }
470 	} else
471 	    fputs("Cannot reopen output file in binary", stderr);
472 	/* and carry on, hoping for the best ! */
473     }
474 #if defined(MSDOS) || defined (_WIN32) || defined(OS2)
475 # ifdef _WIN32
476     else if (!outstr && (term->flags & TERM_BINARY))
477 # else
478     else if (!outstr && !interactive && (term->flags & TERM_BINARY))
479 # endif
480 	{
481 #if defined(_WIN32) && !defined(WGP_CONSOLE)
482 #ifdef PIPES
483 	    if (!output_pipe_open)
484 #endif
485 		if (outstr == NULL && !(term->flags & TERM_NO_OUTPUTFILE))
486 		    int_error(c_token, "cannot output binary data to wgnuplot text window");
487 #endif
488 	    /* binary to stdout in non-interactive session... */
489 	    fflush(stdout);
490 	    setmode(fileno(stdout), O_BINARY);
491 	}
492 #endif
493 
494     if (!term_initialised || term_force_init) {
495 	FPRINTF((stderr, "- calling term->init()\n"));
496 	(*term->init) ();
497 	term_initialised = TRUE;
498 #ifdef HAVE_LOCALE_H
499 	/* This is here only from an abundance of caution (a.k.a. paranoia).
500 	 * Some terminals (wxt qt caca) are known to change the locale when
501 	 * initialized.  Others have been implicated (gd).  Rather than trying
502 	 * to catch all such offenders one by one, cover for all of them here.
503 	 */
504 	setlocale(LC_NUMERIC, "C");
505 #endif
506     }
507 }
508 
509 
510 void
term_start_plot()511 term_start_plot()
512 {
513     FPRINTF((stderr, "term_start_plot()\n"));
514 
515     if (!term_initialised)
516 	term_initialise();
517 
518     if (!term_graphics) {
519 	FPRINTF((stderr, "- calling term->graphics()\n"));
520 	(*term->graphics) ();
521 	term_graphics = TRUE;
522     } else if (multiplot && term_suspended) {
523 	if (term->resume) {
524 	    FPRINTF((stderr, "- calling term->resume()\n"));
525 	    (*term->resume) ();
526 	}
527 	term_suspended = FALSE;
528     }
529 
530     /* Sync point for epslatex text positioning */
531     (*term->layer)(TERM_LAYER_RESET);
532 
533     /* Because PostScript plots may be viewed out of order, make sure */
534     /* Each new plot makes no assumption about the previous palette.  */
535     if (term->flags & TERM_IS_POSTSCRIPT)
536 	invalidate_palette();
537 
538     /* Set canvas size to full range of current terminal coordinates */
539 	canvas.xleft  = 0;
540 	canvas.xright = term->xmax - 1;
541 	canvas.ybot   = 0;
542 	canvas.ytop   = term->ymax - 1;
543 
544 }
545 
546 void
term_end_plot()547 term_end_plot()
548 {
549     FPRINTF((stderr, "term_end_plot()\n"));
550 
551     if (!term_initialised)
552 	return;
553 
554     /* Sync point for epslatex text positioning */
555     (*term->layer)(TERM_LAYER_END_TEXT);
556 
557     if (!multiplot) {
558 	FPRINTF((stderr, "- calling term->text()\n"));
559 	(*term->text) ();
560 	term_graphics = FALSE;
561     } else {
562 	multiplot_next();
563     }
564 #ifdef VMS
565     if (opened_binary)
566 	fflush_binary();
567     else
568 #endif /* VMS */
569 
570 	(void) fflush(gpoutfile);
571 
572 #ifdef USE_MOUSE
573     recalc_statusline();
574     update_ruler();
575 #endif
576 }
577 
578 
579 static void
term_suspend()580 term_suspend()
581 {
582     FPRINTF((stderr, "term_suspend()\n"));
583     if (term_initialised && !term_suspended && term->suspend) {
584 	FPRINTF((stderr, "- calling term->suspend()\n"));
585 	(*term->suspend) ();
586 	term_suspended = TRUE;
587     }
588 }
589 
590 void
term_reset()591 term_reset()
592 {
593     FPRINTF((stderr, "term_reset()\n"));
594 
595 #ifdef USE_MOUSE
596     /* Make sure that ^C will break out of a wait for 'pause mouse' */
597     paused_for_mouse = 0;
598 #ifdef _WIN32
599     kill_pending_Pause_dialog();
600 #endif
601 #endif
602 
603     if (!term_initialised)
604 	return;
605 
606     if (term_suspended) {
607 	if (term->resume) {
608 	    FPRINTF((stderr, "- calling term->resume()\n"));
609 	    (*term->resume) ();
610 	}
611 	term_suspended = FALSE;
612     }
613     if (term_graphics) {
614 	(*term->text) ();
615 	term_graphics = FALSE;
616     }
617     if (term_initialised) {
618 	(*term->reset) ();
619 	term_initialised = FALSE;
620 	/* switch off output to special postscript file (if used) */
621 	gppsfile = NULL;
622     }
623 }
624 
625 void
term_apply_lp_properties(struct lp_style_type * lp)626 term_apply_lp_properties(struct lp_style_type *lp)
627 {
628     /*  This function passes all the line and point properties to the
629      *  terminal driver and issues the corresponding commands.
630      *
631      *  Alas, sometimes it might be necessary to give some help to
632      *  this function by explicitly issuing additional '(*term)(...)'
633      *  commands.
634      */
635     int lt = lp->l_type;
636     int dt = lp->d_type;
637     t_dashtype custom_dash_pattern = lp->custom_dash_pattern;
638     t_colorspec colorspec = lp->pm3d_color;
639 
640     if ((lp->flags & LP_SHOW_POINTS)) {
641 	/* change points, too
642 	 * Currently, there is no 'pointtype' function.  For points
643 	 * there is a special function also dealing with (x,y) co-
644 	 * ordinates.
645 	 */
646 	if (lp->p_size < 0)
647 	    (*term->pointsize) (pointsize);
648 	else
649 	    (*term->pointsize) (lp->p_size);
650     }
651     /*  _first_ set the line width, _then_ set the line type !
652      *  The linetype might depend on the linewidth in some terminals.
653      */
654     (*term->linewidth) (lp->l_width);
655 
656     /* LT_DEFAULT (used only by "set errorbars"?) means don't change it */
657     /* FIXME: If this causes problems, test also for LP_ERRORBAR_SET    */
658     if (lt == LT_DEFAULT)
659 	;
660     else
661 
662     /* The paradigm for handling linetype and dashtype in version 5 is */
663     /* linetype < 0 (e.g. LT_BACKGROUND, LT_NODRAW) means some special */
664     /* category that will be handled directly by term->linetype().     */
665     /* linetype > 0 is now redundant. It used to encode both a color   */
666     /* and a dash pattern.  Now we have separate mechanisms for those. */
667     if (LT_COLORFROMCOLUMN < lt && lt < 0)
668 	(*term->linetype) (lt);
669     else if (term->set_color == null_set_color) {
670 	(*term->linetype) (lt-1);
671 	return;
672     } else /* All normal lines will be solid unless a dashtype is given */
673 	(*term->linetype) (LT_SOLID);
674 
675     /* Apply dashtype or user-specified dash pattern, which may override  */
676     /* the terminal-specific dot/dash pattern belonging to this linetype. */
677     if (lt == LT_AXIS)
678 	; /* LT_AXIS is a special linetype that may incorporate a dash pattern */
679     else if (dt == DASHTYPE_CUSTOM)
680 	(*term->dashtype) (dt, &custom_dash_pattern);
681     else if (dt == DASHTYPE_SOLID)
682 	(*term->dashtype) (dt, NULL);
683     else if (dt >= 0)
684 	/* The null_dashtype() routine or a version 5 terminal's private  */
685 	/* dashtype routine converts this into a call to term->linetype() */
686 	/* yielding the same result as in version 4 except possibly for a */
687 	/* different line width.					  */
688 	(*term->dashtype) (dt, NULL);
689 
690     /* Finally adjust the color of the line */
691     apply_pm3dcolor(&colorspec);
692 }
693 
694 void
term_start_multiplot()695 term_start_multiplot()
696 {
697     FPRINTF((stderr, "term_start_multiplot()\n"));
698     multiplot_start();
699 #ifdef USE_MOUSE
700     UpdateStatusline();
701 #endif
702 }
703 
704 void
term_end_multiplot()705 term_end_multiplot()
706 {
707     FPRINTF((stderr, "term_end_multiplot()\n"));
708     if (!multiplot)
709 	return;
710 
711     if (term_suspended) {
712 	if (term->resume)
713 	    (*term->resume) ();
714 	term_suspended = FALSE;
715     }
716 
717     multiplot_end();
718 
719     term_end_plot();
720 #ifdef USE_MOUSE
721     UpdateStatusline();
722 #endif
723 }
724 
725 void
term_check_multiplot_okay(TBOOLEAN f_interactive)726 term_check_multiplot_okay(TBOOLEAN f_interactive)
727 {
728     FPRINTF((stderr, "term_multiplot_okay(%d)\n", f_interactive));
729 
730     if (!term_initialised)
731 	return;                 /* they've not started yet */
732 
733     /* make sure that it is safe to issue an interactive prompt
734      * it is safe if
735      *   it is not an interactive read, or
736      *   the terminal supports interactive multiplot, or
737      *   we are not writing to stdout and terminal doesn't
738      *     refuse multiplot outright
739      */
740     if (!f_interactive || (term->flags & TERM_CAN_MULTIPLOT) ||
741 	((gpoutfile != stdout) && !(term->flags & TERM_CANNOT_MULTIPLOT))
742 	) {
743 	/* it's okay to use multiplot here, but suspend first */
744 	term_suspend();
745 	return;
746     }
747     /* uh oh: they're not allowed to be in multiplot here */
748 
749     term_end_multiplot();
750 
751     /* at this point we know that it is interactive and that the
752      * terminal can either only do multiplot when writing to
753      * to a file, or it does not do multiplot at all
754      */
755 
756     if (term->flags & TERM_CANNOT_MULTIPLOT)
757 	int_error(NO_CARET, "This terminal does not support multiplot");
758     else
759 	int_error(NO_CARET, "Must set output to a file or put all multiplot commands on one input line");
760 }
761 
762 
763 void
write_multiline(unsigned int x,unsigned int y,char * text,JUSTIFY hor,VERT_JUSTIFY vert,int angle,const char * font)764 write_multiline(
765     unsigned int x, unsigned int y,
766     char *text,
767     JUSTIFY hor,                /* horizontal ... */
768     VERT_JUSTIFY vert,          /* ... and vertical just - text in hor direction despite angle */
769     int angle,                  /* assume term has already been set for this */
770     const char *font)           /* NULL or "" means use default */
771 {
772     struct termentry *t = term;
773     char *p = text;
774 
775     if (!p)
776 	return;
777 
778     /* EAM 9-Feb-2003 - Set font before calculating sizes */
779     if (font && *font)
780 	(*t->set_font) (font);
781 
782     if (vert != JUST_TOP) {
783 	/* count lines and adjust y */
784 	int lines = 0;          /* number of linefeeds - one fewer than lines */
785 	while (*p) {
786 	    if (*p++ == '\n')
787 		++lines;
788 	}
789 	if (angle)
790 	    x -= (vert * lines * t->v_char) / 2;
791 	else
792 	    y += (vert * lines * t->v_char) / 2;
793     }
794 
795     for (;;) {                  /* we will explicitly break out */
796 
797 	if ((text != NULL) && (p = strchr(text, '\n')) != NULL)
798 	    *p = 0;             /* terminate the string */
799 
800 	if ((*t->justify_text) (hor)) {
801 	    if (on_page(x, y))
802 		(*t->put_text) (x, y, text);
803 	} else {
804 	    int len = estimate_strlen(text);
805 	    int hfix, vfix;
806 
807 	    if (angle == 0) {
808 		hfix = hor * t->h_char * len / 2;
809 		vfix = 0;
810 	    } else {
811 		/* Attention: This relies on the numeric values of enum JUSTIFY! */
812 		hfix = hor * t->h_char * len * cos(angle * DEG2RAD) / 2 + 0.5;
813 		vfix = hor * t->v_char * len * sin(angle * DEG2RAD) / 2 + 0.5;
814 	    }
815 		if (on_page(x - hfix, y - vfix))
816 		    (*t->put_text) (x - hfix, y - vfix, text);
817 	}
818 	if (angle == 90 || angle == TEXT_VERTICAL)
819 	    x += t->v_char;
820 	else if (angle == -90 || angle == -TEXT_VERTICAL)
821 	    x -= t->v_char;
822 	else
823 	    y -= t->v_char;
824 
825 	if (!p)
826 	    break;
827 	else {
828 	    /* put it back */
829 	    *p = '\n';
830 	}
831 
832 	text = p + 1;
833     }                           /* unconditional branch back to the for(;;) - just a goto ! */
834 
835     if (font && *font)
836 	(*t->set_font) ("");
837 
838 }
839 
840 
841 static void
do_point(unsigned int x,unsigned int y,int number)842 do_point(unsigned int x, unsigned int y, int number)
843 {
844     int htic, vtic;
845     struct termentry *t = term;
846 
847     /* use solid lines for point symbols */
848     if (term->dashtype != null_dashtype)
849 	term->dashtype(DASHTYPE_SOLID, NULL);
850 
851     if (number < 0) {           /* do dot */
852 	(*t->move) (x, y);
853 	(*t->vector) (x, y);
854 	return;
855     }
856     number %= POINT_TYPES;
857     /* should be in term_tbl[] in later version */
858     htic = (term_pointsize * t->h_tic / 2);
859     vtic = (term_pointsize * t->v_tic / 2);
860 
861     /* point types 1..4 are same as in postscript, png and x11
862        point types 5..6 are "similar"
863        (note that (number) equals (pointtype-1)
864     */
865     switch (number) {
866     case 4:                     /* do diamond */
867 	(*t->move) (x - htic, y);
868 	(*t->vector) (x, y - vtic);
869 	(*t->vector) (x + htic, y);
870 	(*t->vector) (x, y + vtic);
871 	(*t->vector) (x - htic, y);
872 	(*t->move) (x, y);
873 	(*t->vector) (x, y);
874 	break;
875     case 0:                     /* do plus */
876 	(*t->move) (x - htic, y);
877 	(*t->vector) (x - htic, y);
878 	(*t->vector) (x + htic, y);
879 	(*t->move) (x, y - vtic);
880 	(*t->vector) (x, y - vtic);
881 	(*t->vector) (x, y + vtic);
882 	break;
883     case 3:                     /* do box */
884 	(*t->move) (x - htic, y - vtic);
885 	(*t->vector) (x - htic, y - vtic);
886 	(*t->vector) (x + htic, y - vtic);
887 	(*t->vector) (x + htic, y + vtic);
888 	(*t->vector) (x - htic, y + vtic);
889 	(*t->vector) (x - htic, y - vtic);
890 	(*t->move) (x, y);
891 	(*t->vector) (x, y);
892 	break;
893     case 1:                     /* do X */
894 	(*t->move) (x - htic, y - vtic);
895 	(*t->vector) (x - htic, y - vtic);
896 	(*t->vector) (x + htic, y + vtic);
897 	(*t->move) (x - htic, y + vtic);
898 	(*t->vector) (x - htic, y + vtic);
899 	(*t->vector) (x + htic, y - vtic);
900 	break;
901     case 5:                     /* do triangle */
902 	(*t->move) (x, y + (4 * vtic / 3));
903 	(*t->vector) (x - (4 * htic / 3), y - (2 * vtic / 3));
904 	(*t->vector) (x + (4 * htic / 3), y - (2 * vtic / 3));
905 	(*t->vector) (x, y + (4 * vtic / 3));
906 	(*t->move) (x, y);
907 	(*t->vector) (x, y);
908 	break;
909     case 2:                     /* do star */
910 	(*t->move) (x - htic, y);
911 	(*t->vector) (x - htic, y);
912 	(*t->vector) (x + htic, y);
913 	(*t->move) (x, y - vtic);
914 	(*t->vector) (x, y - vtic);
915 	(*t->vector) (x, y + vtic);
916 	(*t->move) (x - htic, y - vtic);
917 	(*t->vector) (x - htic, y - vtic);
918 	(*t->vector) (x + htic, y + vtic);
919 	(*t->move) (x - htic, y + vtic);
920 	(*t->vector) (x - htic, y + vtic);
921 	(*t->vector) (x + htic, y - vtic);
922 	break;
923     }
924 }
925 
926 static void
do_pointsize(double size)927 do_pointsize(double size)
928 {
929     term_pointsize = (size >= 0 ? size : 1);
930 }
931 
932 
933 /*
934  * general point routine
935  */
936 static void
line_and_point(unsigned int x,unsigned int y,int number)937 line_and_point(unsigned int x, unsigned int y, int number)
938 {
939     /* temporary(?) kludge to allow terminals with bad linetypes
940        to make nice marks */
941 
942     (*term->linetype) (NICE_LINE);
943     do_point(x, y, number);
944 }
945 
946 /*
947  * general arrow routine
948  *
949  * I set the angle between the arrowhead and the line 15 degree.
950  * The length of arrowhead varies depending on the line length
951  * within the the range [0.3*(the-tic-length), 2*(the-tic-length)].
952  * No head is printed if the arrow length is zero.
953  *
954  *            Yasu-hiro Yamazaki(hiro@rainbow.physics.utoronto.ca)
955  *            Jul 1, 1993
956  */
957 
958 #define COS15 (0.96593)         /* cos of 15 degree */
959 #define SIN15 (0.25882)         /* sin of 15 degree */
960 
961 #define HEAD_LONG_LIMIT  (2.0)  /* long  limit of arrowhead length */
962 #define HEAD_SHORT_LIMIT (0.3)  /* short limit of arrowhead length */
963 				/* their units are the "tic" length */
964 
965 #define HEAD_COEFF  (0.3)       /* default value of head/line length ratio */
966 
967 int curr_arrow_headlength; /* access head length + angle without changing API */
968 double curr_arrow_headangle;    /* angle in degrees */
969 double curr_arrow_headbackangle;  /* angle in degrees */
970 arrowheadfill curr_arrow_headfilled;      /* arrow head filled or not */
971 TBOOLEAN curr_arrow_headfixedsize;        /* Adapt the head size for short arrows or not */
972 
973 static void
do_arrow(unsigned int usx,unsigned int usy,unsigned int uex,unsigned int uey,int headstyle)974 do_arrow(
975     unsigned int usx, unsigned int usy,   /* start point */
976     unsigned int uex, unsigned int uey,   /* end point (point of arrowhead) */
977     int headstyle)
978 {
979     /* Clipping and angle calculations do not work if coords are unsigned! */
980     int sx = (int)usx;
981     int sy = (int)usy;
982     int ex = (int)uex;
983     int ey = (int)uey;
984 
985     struct termentry *t = term;
986     float len_tic = ((double) (t->h_tic + t->v_tic)) / 2.0;
987     /* average of tic sizes */
988     /* (dx,dy) : vector from end to start */
989     double dx = sx - ex;
990     double dy = sy - ey;
991     double len_arrow = sqrt(dx * dx + dy * dy);
992     gpiPoint head_points[5];
993     int xm = 0, ym = 0;
994     BoundingBox *clip_save;
995     t_arrow_head head = (t_arrow_head)((headstyle < 0) ? -headstyle : headstyle);
996 	/* negative headstyle means draw heads only, no shaft */
997 
998     /* The arrow shaft was clipped already in do_clip_arrow() but we still */
999     /* need to clip the head here. */
1000     clip_save = clip_area;
1001     if (term->flags & TERM_CAN_CLIP)
1002 	clip_area = NULL;
1003     else
1004 	clip_area = &canvas;
1005 
1006     /* Calculate and draw arrow heads.
1007      * Draw no head for arrows with length = 0, or, to be more specific,
1008      * length < DBL_EPSILON, because len_arrow will almost always be != 0.
1009      */
1010     if ((head != NOHEAD) && fabs(len_arrow) >= DBL_EPSILON) {
1011 	int x1, y1, x2, y2;
1012 	if (curr_arrow_headlength <= 0) {
1013 	    /* An arrow head with the default size and angles */
1014 	    double coeff_shortest = len_tic * HEAD_SHORT_LIMIT / len_arrow;
1015 	    double coeff_longest = len_tic * HEAD_LONG_LIMIT / len_arrow;
1016 	    double head_coeff = GPMAX(coeff_shortest,
1017 				      GPMIN(HEAD_COEFF, coeff_longest));
1018 	    /* we put the arrowhead marks at 15 degrees to line */
1019 	    x1 = (int) ((COS15 * dx - SIN15 * dy) * head_coeff);
1020 	    y1 = (int) ((SIN15 * dx + COS15 * dy) * head_coeff);
1021 	    x2 = (int) ((COS15 * dx + SIN15 * dy) * head_coeff);
1022 	    y2 = (int) ((-SIN15 * dx + COS15 * dy) * head_coeff);
1023 	    /* backangle defaults to 90 deg */
1024 	    xm = (int) ((x1 + x2)/2);
1025 	    ym = (int) ((y1 + y2)/2);
1026 	} else {
1027 	    /* An arrow head with the length + angle specified explicitly.	*/
1028 	    /* Assume that if the arrow is shorter than the arrowhead, this is	*/
1029 	    /* because of foreshortening in a 3D plot.                  	*/
1030 	    double alpha = curr_arrow_headangle * DEG2RAD;
1031 	    double beta = curr_arrow_headbackangle * DEG2RAD;
1032 	    double phi = atan2(-dy,-dx); /* azimuthal angle of the vector */
1033 	    double backlen, effective_length;
1034 	    double dx2, dy2;
1035 
1036 	    effective_length = curr_arrow_headlength;
1037 	    if (!curr_arrow_headfixedsize && (curr_arrow_headlength > len_arrow/2.)) {
1038 		effective_length = len_arrow/2.;
1039 		alpha = atan(tan(alpha)*((double)curr_arrow_headlength/effective_length));
1040 		beta = atan(tan(beta)*((double)curr_arrow_headlength/effective_length));
1041 	    }
1042 	    backlen = sin(alpha) / sin(beta);
1043 
1044 	    /* anticlock-wise head segment */
1045 	    x1 = -(int)(effective_length * cos( alpha - phi ));
1046 	    y1 =  (int)(effective_length * sin( alpha - phi ));
1047 	    /* clock-wise head segment */
1048 	    dx2 = -effective_length * cos( phi + alpha );
1049 	    dy2 = -effective_length * sin( phi + alpha );
1050 	    x2 = (int) (dx2);
1051 	    y2 = (int) (dy2);
1052 	    /* back point */
1053 	    xm = (int) (dx2 + backlen*effective_length * cos( phi + beta ));
1054 	    ym = (int) (dy2 + backlen*effective_length * sin( phi + beta ));
1055 	}
1056 
1057 	if ((head & END_HEAD) && !clip_point(ex, ey)) {
1058 	    head_points[0].x = ex + xm;
1059 	    head_points[0].y = ey + ym;
1060 	    head_points[1].x = ex + x1;
1061 	    head_points[1].y = ey + y1;
1062 	    head_points[2].x = ex;
1063 	    head_points[2].y = ey;
1064 	    head_points[3].x = ex + x2;
1065 	    head_points[3].y = ey + y2;
1066 	    head_points[4].x = ex + xm;
1067 	    head_points[4].y = ey + ym;
1068 	    if (curr_arrow_headfilled >= AS_FILLED) {
1069 		/* draw filled forward arrow head */
1070 		head_points->style = FS_OPAQUE;
1071 		if (t->filled_polygon)
1072 		    (*t->filled_polygon) (5, head_points);
1073 	    }
1074 	    /* draw outline of forward arrow head */
1075 	    if (curr_arrow_headfilled == AS_NOFILL) {
1076 		draw_clip_polygon(3, head_points+1);
1077 	    } else if (curr_arrow_headfilled != AS_NOBORDER) {
1078 		draw_clip_polygon(5, head_points);
1079 	    }
1080 	}
1081 
1082 	/* backward arrow head */
1083 	if ((head & BACKHEAD) && !clip_point(sx,sy)) {
1084 	    head_points[0].x = sx - xm;
1085 	    head_points[0].y = sy - ym;
1086 	    head_points[1].x = sx - x1;
1087 	    head_points[1].y = sy - y1;
1088 	    head_points[2].x = sx;
1089 	    head_points[2].y = sy;
1090 	    head_points[3].x = sx - x2;
1091 	    head_points[3].y = sy - y2;
1092 	    head_points[4].x = sx - xm;
1093 	    head_points[4].y = sy - ym;
1094 	    if (curr_arrow_headfilled >= AS_FILLED) {
1095 		/* draw filled backward arrow head */
1096 		head_points->style = FS_OPAQUE;
1097 		if (t->filled_polygon)
1098 		    (*t->filled_polygon) (5, head_points);
1099 	    }
1100 	    /* draw outline of backward arrow head */
1101 	    if (curr_arrow_headfilled == AS_NOFILL) {
1102 		draw_clip_polygon(3, head_points+1);
1103 	    } else if (curr_arrow_headfilled != AS_NOBORDER) {
1104 		draw_clip_polygon(5, head_points);
1105 	    }
1106 	}
1107     }
1108 
1109     /* Draw the line for the arrow. */
1110     if (headstyle >= 0) {
1111 	if ((head & BACKHEAD)
1112 	&&  (fabs(len_arrow) >= DBL_EPSILON) && (curr_arrow_headfilled != AS_NOFILL) ) {
1113 	    sx -= xm;
1114 	    sy -= ym;
1115 	}
1116 	if ((head & END_HEAD)
1117 	&&  (fabs(len_arrow) >= DBL_EPSILON) && (curr_arrow_headfilled != AS_NOFILL) ) {
1118 	    ex += xm;
1119 	    ey += ym;
1120 	}
1121 	draw_clip_line(sx, sy, ex, ey);
1122     }
1123 
1124     /* Restore previous clipping box */
1125     clip_area = clip_save;
1126 
1127 }
1128 
1129 #ifdef EAM_OBJECTS
1130 /* Generic routine for drawing circles or circular arcs.          */
1131 /* If this feature proves useful, we can add a new terminal entry */
1132 /* point term->arc() to the API and let terminals either provide  */
1133 /* a private implemenation or use this generic one.               */
1134 
1135 void
do_arc(int cx,int cy,double radius,double arc_start,double arc_end,int style,TBOOLEAN wedge)1136 do_arc(
1137     int cx, int cy, /* Center */
1138     double radius, /* Radius */
1139     double arc_start, double arc_end, /* Limits of arc in degress */
1140     int style, TBOOLEAN wedge)
1141 {
1142     gpiPoint vertex[250];
1143     int i, segments;
1144     double aspect;
1145     TBOOLEAN complete_circle;
1146 
1147     /* Protect against out-of-range values */
1148     while (arc_start < 0)
1149 	arc_start += 360.;
1150     while (arc_end > 360.)
1151 	arc_end -= 360.;
1152 
1153     /* Always draw counterclockwise */
1154     while (arc_end < arc_start)
1155 	arc_end += 360.;
1156 
1157     /* Choose how finely to divide this arc into segments */
1158     /* FIXME: INC=2 causes problems for gnuplot_x11 */
1159 #   define INC 3.
1160     segments = (arc_end - arc_start) / INC;
1161     if (segments < 1)
1162 	segments = 1;
1163 
1164     /* Calculate the vertices */
1165     aspect = (double)term->v_tic / (double)term->h_tic;
1166     for (i=0; i<segments; i++) {
1167 	vertex[i].x = cx + cos(DEG2RAD * (arc_start + i*INC)) * radius;
1168 	vertex[i].y = cy + sin(DEG2RAD * (arc_start + i*INC)) * radius * aspect;
1169     }
1170 #   undef INC
1171     vertex[segments].x = cx + cos(DEG2RAD * arc_end) * radius;
1172     vertex[segments].y = cy + sin(DEG2RAD * arc_end) * radius * aspect;
1173 
1174     if (fabs(arc_end - arc_start) > .1
1175     &&  fabs(arc_end - arc_start) < 359.9) {
1176 	vertex[++segments].x = cx;
1177 	vertex[segments].y = cy;
1178 	vertex[++segments].x = vertex[0].x;
1179 	vertex[segments].y = vertex[0].y;
1180 	complete_circle = FALSE;
1181     } else
1182 	complete_circle = TRUE;
1183 
1184     if (style) { /* Fill in the center */
1185 	gpiPoint fillarea[250];
1186 	int in;
1187 
1188 	clip_polygon(vertex, fillarea, segments, &in);
1189 	fillarea[0].style = style;
1190 	if (term->filled_polygon)
1191 	    term->filled_polygon(in, fillarea);
1192 
1193     } else { /* Draw the arc */
1194 	if (!wedge && !complete_circle)
1195 	    segments -= 2;
1196 	draw_clip_polygon(segments+1, vertex);
1197     }
1198 }
1199 #endif /* EAM_OBJECTS */
1200 
1201 
1202 
1203 #define TERM_PROTO
1204 #define TERM_BODY
1205 #define TERM_PUBLIC static
1206 
1207 #include "term.h"
1208 
1209 #undef TERM_PROTO
1210 #undef TERM_BODY
1211 #undef TERM_PUBLIC
1212 
1213 
1214 /* Dummy functions for unavailable features */
1215 /* return success if they asked for default - this simplifies code
1216  * where param is passed as a param. Client can first pass it here,
1217  * and only if it fails do they have to see what was trying to be done
1218  */
1219 
1220 /* change angle of text.  0 is horizontal left to right.
1221    * 1 is vertical bottom to top (90 deg rotate)
1222  */
1223 static int
null_text_angle(int ang)1224 null_text_angle(int ang)
1225 {
1226     return (ang == 0);
1227 }
1228 
1229 /* change justification of text.
1230  * modes are LEFT (flush left), CENTRE (centred), RIGHT (flush right)
1231  */
1232 static int
null_justify_text(enum JUSTIFY just)1233 null_justify_text(enum JUSTIFY just)
1234 {
1235     return (just == LEFT);
1236 }
1237 
1238 
1239 /*
1240  * Deprecated terminal function (pre-version 3)
1241  */
1242 static int
null_scale(double x,double y)1243 null_scale(double x, double y)
1244 {
1245     (void) x;                   /* avoid -Wunused warning */
1246     (void) y;
1247     int_error(NO_CARET, "Attempt to call deprecated terminal function");
1248     return FALSE;               /* can't be done */
1249 }
1250 
1251 static void
null_layer(t_termlayer layer)1252 null_layer(t_termlayer layer)
1253 {
1254     (void) layer;               /* avoid -Wunused warning */
1255 }
1256 
1257 static void
options_null()1258 options_null()
1259 {
1260     term_options[0] = '\0';     /* we have no options */
1261 }
1262 
1263 static void
graphics_null()1264 graphics_null()
1265 {
1266     fprintf(stderr,
1267 	    "WARNING: Plotting with an 'unknown' terminal.\n"
1268 	    "No output will be generated. Please select a terminal with 'set terminal'.\n");
1269 }
1270 
1271 static void
UNKNOWN_null()1272 UNKNOWN_null()
1273 {
1274 }
1275 
1276 static void
MOVE_null(unsigned int x,unsigned int y)1277 MOVE_null(unsigned int x, unsigned int y)
1278 {
1279     (void) x;                   /* avoid -Wunused warning */
1280     (void) y;
1281 }
1282 
1283 static void
LINETYPE_null(int t)1284 LINETYPE_null(int t)
1285 {
1286     (void) t;                   /* avoid -Wunused warning */
1287 }
1288 
1289 static void
PUTTEXT_null(unsigned int x,unsigned int y,const char * s)1290 PUTTEXT_null(unsigned int x, unsigned int y, const char *s)
1291 {
1292     (void) s;                   /* avoid -Wunused warning */
1293     (void) x;
1294     (void) y;
1295 }
1296 
1297 
1298 static void
null_linewidth(double s)1299 null_linewidth(double s)
1300 {
1301     (void) s;                   /* avoid -Wunused warning */
1302 }
1303 
1304 static int
null_set_font(const char * font)1305 null_set_font(const char *font)
1306 {
1307     (void) font;		/* avoid -Wunused warning */
1308     return FALSE;		/* Never used!! */
1309 }
1310 
1311 static void
null_set_color(struct t_colorspec * colorspec)1312 null_set_color(struct t_colorspec *colorspec)
1313 {
1314     if (colorspec->type == TC_LT)
1315 	term->linetype(colorspec->lt);
1316 }
1317 
1318 static void
null_dashtype(int type,t_dashtype * custom_dash_pattern)1319 null_dashtype(int type, t_dashtype *custom_dash_pattern)
1320 {
1321     (void) custom_dash_pattern;	/* ignore */
1322     /*
1323      * If the terminal does not support user-defined dashtypes all we can do
1324      * do is fall through to the old (pre-v5) assumption that the dashtype,
1325      * if any, is part of the linetype.  We also assume that the color will
1326      * be adjusted after this.
1327      */
1328     if (type <= 0)
1329 	type = LT_SOLID;
1330     term->linetype(type);
1331 }
1332 
1333 /* setup the magic macros to compile in the right parts of the
1334  * terminal drivers included by term.h
1335  */
1336 #define TERM_TABLE
1337 #define TERM_TABLE_START(x) ,{
1338 #define TERM_TABLE_END(x)   }
1339 
1340 
1341 /*
1342  * term_tbl[] contains an entry for each terminal.  "unknown" must be the
1343  *   first, since term is initialized to 0.
1344  */
1345 static struct termentry term_tbl[] =
1346 {
1347     {"unknown", "Unknown terminal type - not a plotting device",
1348      100, 100, 1, 1,
1349      1, 1, options_null, UNKNOWN_null, UNKNOWN_null,
1350      UNKNOWN_null, null_scale, graphics_null, MOVE_null, MOVE_null,
1351      LINETYPE_null, PUTTEXT_null}
1352 
1353 #include "term.h"
1354 
1355 };
1356 
1357 #define TERMCOUNT (sizeof(term_tbl) / sizeof(term_tbl[0]))
1358 
1359 void
list_terms()1360 list_terms()
1361 {
1362     int i;
1363     char *line_buffer = gp_alloc(BUFSIZ, "list_terms");
1364     int sort_idxs[TERMCOUNT];
1365 
1366     /* sort terminal types alphabetically */
1367     for( i = 0; i < TERMCOUNT; i++ )
1368 	sort_idxs[i] = i;
1369     qsort( sort_idxs, TERMCOUNT, sizeof(int), termcomp );
1370     /* now sort_idxs[] contains the sorted indices */
1371 
1372     StartOutput();
1373     strcpy(line_buffer, "\nAvailable terminal types:\n");
1374     OutLine(line_buffer);
1375 
1376     for (i = 0; i < TERMCOUNT; i++) {
1377 	sprintf(line_buffer, "  %15s  %s\n",
1378 		term_tbl[sort_idxs[i]].name,
1379 		term_tbl[sort_idxs[i]].description);
1380 	OutLine(line_buffer);
1381     }
1382 
1383     EndOutput();
1384     free(line_buffer);
1385 }
1386 
1387 /* Return string with all terminal names.
1388    Note: caller must free the returned names after use.
1389 */
1390 char*
get_terminals_names()1391 get_terminals_names()
1392 {
1393     int i;
1394     char *buf = gp_alloc(TERMCOUNT*15, "all_term_names"); /* max 15 chars per name */
1395     char *names;
1396     int sort_idxs[TERMCOUNT];
1397 
1398     /* sort terminal types alphabetically */
1399     for( i = 0; i < TERMCOUNT; i++ )
1400 	sort_idxs[i] = i;
1401     qsort( sort_idxs, TERMCOUNT, sizeof(int), termcomp );
1402     /* now sort_idxs[] contains the sorted indices */
1403 
1404     strcpy(buf, " "); /* let the string have leading and trailing " " in order to search via strstrt(GPVAL_TERMINALS, " png "); */
1405     for (i = 0; i < TERMCOUNT; i++)
1406 	sprintf(buf+strlen(buf), "%s ", term_tbl[sort_idxs[i]].name);
1407     names = gp_alloc(strlen(buf)+1, "all_term_names2");
1408     strcpy(names, buf);
1409     free(buf);
1410     return names;
1411 }
1412 
1413 static int
termcomp(const generic * arga,const generic * argb)1414 termcomp(const generic *arga, const generic *argb)
1415 {
1416     const int *a = arga;
1417     const int *b = argb;
1418 
1419     return( strcasecmp( term_tbl[*a].name, term_tbl[*b].name ) );
1420 }
1421 
1422 /* set_term: get terminal number from name on command line
1423  * will change 'term' variable if successful
1424  */
1425 struct termentry *
set_term()1426 set_term()
1427 {
1428     struct termentry *t = NULL;
1429     char *input_name = NULL;
1430 
1431     if (!END_OF_COMMAND) {
1432 	input_name = gp_input_line + token[c_token].start_index;
1433 	t = change_term(input_name, token[c_token].length);
1434 	if (!t && isstringvalue(c_token) && (input_name = try_to_get_string())) {
1435 	    if (strchr(input_name, ' '))
1436 		*strchr(input_name, ' ') = '\0';
1437 	    t = change_term(input_name, strlen(input_name));
1438 	    free(input_name);
1439 	} else {
1440 	    c_token++;
1441 	}
1442     }
1443 
1444     if (!t) {
1445 	change_term("unknown", 7);
1446 	int_error(c_token-1, "unknown or ambiguous terminal type; type just 'set terminal' for a list");
1447     }
1448 
1449     /* otherwise the type was changed */
1450     return (t);
1451 }
1452 
1453 /* change_term: get terminal number from name and set terminal type
1454  *
1455  * returns NULL for unknown or ambiguous, otherwise is terminal
1456  * driver pointer
1457  */
1458 struct termentry *
change_term(const char * origname,int length)1459 change_term(const char *origname, int length)
1460 {
1461     int i;
1462     struct termentry *t = NULL;
1463     TBOOLEAN ambiguous = FALSE;
1464 
1465     /* For backwards compatibility only */
1466     char *name = (char *)origname;
1467     if (!strncmp(origname,"X11",length)) {
1468 	name = "x11";
1469 	length = 3;
1470     }
1471 
1472 #ifdef HAVE_CAIROPDF
1473     /* To allow "set term eps" as short for "set term epscairo" */
1474     if (!strncmp(origname,"eps",length)) {
1475 	name = "epscairo";
1476 	length = 8;
1477     }
1478 #endif
1479 
1480     for (i = 0; i < TERMCOUNT; i++) {
1481 	if (!strncmp(name, term_tbl[i].name, length)) {
1482 	    if (t)
1483 		ambiguous = TRUE;
1484 	    t = term_tbl + i;
1485 	    /* Exact match is always accepted */
1486 	    if (length == strlen(term_tbl[i].name)) {
1487 		ambiguous = FALSE;
1488 		break;
1489 	    }
1490 	}
1491     }
1492 
1493     if (!t || ambiguous)
1494 	return (NULL);
1495 
1496     /* Success: set terminal type now */
1497 
1498     term = t;
1499     term_initialised = FALSE;
1500 
1501     /* check that optional fields are initialised to something */
1502     if (term->text_angle == 0)
1503 	term->text_angle = null_text_angle;
1504     if (term->justify_text == 0)
1505 	term->justify_text = null_justify_text;
1506     if (term->point == 0)
1507 	term->point = do_point;
1508     if (term->arrow == 0)
1509 	term->arrow = do_arrow;
1510     if (term->pointsize == 0)
1511 	term->pointsize = do_pointsize;
1512     if (term->linewidth == 0)
1513 	term->linewidth = null_linewidth;
1514     if (term->layer == 0)
1515 	term->layer = null_layer;
1516     if (term->tscale <= 0)
1517 	term->tscale = 1.0;
1518     if (term->set_font == 0)
1519 	term->set_font = null_set_font;
1520     if (term->set_color == 0) {
1521 	term->set_color = null_set_color;
1522 	term->flags |= TERM_NULL_SET_COLOR;
1523     }
1524     if (term->dashtype == 0)
1525 	term->dashtype = null_dashtype;
1526 
1527     if (interactive)
1528 	fprintf(stderr, "\nTerminal type is now '%s'\n", term->name);
1529 
1530     /* Invalidate any terminal-specific structures that may be active */
1531     invalidate_palette();
1532 
1533     return (t);
1534 }
1535 
1536 /*
1537  * Find an appropriate initial terminal type.
1538  * The environment variable GNUTERM is checked first; if that does
1539  * not exist, then the terminal hardware is checked, if possible,
1540  * and finally, we can check $TERM for some kinds of terminals.
1541  * A default can be set with -DDEFAULTTERM=myterm in the Makefile
1542  * or #define DEFAULTTERM myterm in term.h
1543  */
1544 /* thanks to osupyr!alden (Dave Alden) for the original GNUTERM code */
1545 void
init_terminal()1546 init_terminal()
1547 {
1548     char *term_name = DEFAULTTERM;
1549 #if (defined(MSDOS) && !defined(_WIN32)) || defined(SUN) || defined(X11)
1550     char *env_term = NULL;      /* from TERM environment var */
1551 #endif
1552 #ifdef X11
1553     char *display = NULL;
1554 #endif
1555     char *gnuterm = NULL;
1556 
1557     /* GNUTERM environment variable is primary */
1558     gnuterm = getenv("GNUTERM");
1559     if (gnuterm != (char *) NULL) {
1560 	/* April 2017 - allow GNUTERM to include terminal options */
1561 	char *set_term = "set term ";
1562 	char *set_term_command = gp_alloc(strlen(set_term) + strlen(gnuterm) + 4, NULL);
1563 	strcpy(set_term_command, set_term);
1564 	strcat(set_term_command, gnuterm);
1565 	do_string(set_term_command);
1566 	free(set_term_command);
1567 	/* replicate environmental variable GNUTERM for internal use */
1568 	Gstring(&(add_udv_by_name("GNUTERM")->udv_value), gp_strdup(gnuterm));
1569 	return;
1570 
1571     } else {
1572 
1573 #ifdef VMS
1574 	term_name = vms_init();
1575 #endif /* VMS */
1576 
1577 	if (term_name == (char *) NULL
1578             && getenv ("DOMTERM") != NULL)
1579           term_name = "domterm";
1580 
1581 #ifdef __BEOS__
1582 	env_term = getenv("TERM");
1583 	if (term_name == (char *) NULL
1584 	    && env_term != (char *) NULL && strcmp(env_term, "beterm") == 0)
1585 	    term_name = "be";
1586 #endif /* BeOS */
1587 
1588 #if defined(WXWIDGETS) && defined(_WIN32)
1589 	if (term_name == (char *) NULL)
1590 	    term_name = "wxt";
1591 #endif
1592 
1593 #ifdef QTTERM
1594 	if (term_name == (char *) NULL)
1595 	    term_name = "qt";
1596 #endif
1597 
1598 #ifdef WXWIDGETS
1599 	if (term_name == (char *) NULL)
1600 	    term_name = "wxt";
1601 #endif
1602 
1603 #ifdef _WIN32
1604 	if (term_name == (char *) NULL)
1605 	    term_name = "win";
1606 #endif /* _WIN32 */
1607 
1608 #if defined(__APPLE__) && defined(__MACH__) && defined(HAVE_FRAMEWORK_AQUATERM)
1609 	/* Mac OS X with AquaTerm installed */
1610 	term_name = "aqua";
1611 #endif
1612 
1613 #ifdef X11
1614 	env_term = getenv("TERM");      /* try $TERM */
1615 	if (term_name == (char *) NULL
1616 	    && env_term != (char *) NULL && strcmp(env_term, "xterm") == 0)
1617 	    term_name = "x11";
1618 	display = getenv("DISPLAY");
1619 	if (term_name == (char *) NULL && display != (char *) NULL)
1620 	    term_name = "x11";
1621 	if (X11_Display)
1622 	    term_name = "x11";
1623 #endif /* x11 */
1624 
1625 #ifdef DJGPP
1626 	term_name = "svga";
1627 #endif
1628 
1629 #ifdef GRASS
1630 	term_name = "grass";
1631 #endif
1632 
1633 #ifdef OS2
1634 /* amai: Note that we do some checks above and now overwrite any
1635    results. Perhaps we may disable checks above!? */
1636 #ifdef X11
1637 /* WINDOWID is set in sessions like xterm, etc.
1638    DISPLAY is also mandatory. */
1639 	env_term = getenv("WINDOWID");
1640 	display  = getenv("DISPLAY");
1641 	if ((env_term != (char *) NULL) && (display != (char *) NULL))
1642 	    term_name = "x11";
1643 	else
1644 #endif          /* X11 */
1645 	    term_name = "pm";
1646 #endif /*OS2 */
1647 
1648 /* set linux terminal only if LINUX_setup was successfull, if we are on X11
1649    LINUX_setup has failed, also if we are logged in by network */
1650 #ifdef LINUXVGA
1651 	if (LINUX_graphics_allowed)
1652 #if defined(VGAGL) && defined (THREEDKIT)
1653 	    term_name = "vgagl";
1654 #else
1655 	    term_name = "linux";
1656 #endif
1657 #endif /* LINUXVGA */
1658     }
1659 
1660     /* We have a name, try to set term type */
1661     if (term_name != NULL && *term_name != '\0') {
1662 	int namelength = strlen(term_name);
1663 	struct udvt_entry *name = add_udv_by_name("GNUTERM");
1664 
1665 	Gstring(&name->udv_value, gp_strdup(term_name));
1666 
1667 	if (strchr(term_name,' '))
1668 	    namelength = strchr(term_name,' ') - term_name;
1669 
1670 	/* Force the terminal to initialize default fonts, etc.	This prevents */
1671 	/* segfaults and other strangeness if you set GNUTERM to "post" or    */
1672 	/* "png" for example. However, calling X11_options() is expensive due */
1673 	/* to the fork+execute of gnuplot_x11 and x11 can tolerate not being  */
1674 	/* initialized until later.                                           */
1675 	/* Note that gp_input_line[] is blank at this point.	              */
1676 	if (change_term(term_name, namelength)) {
1677 	    if (strcmp(term->name,"x11"))
1678 		term->options();
1679 	    return;
1680 	}
1681 	fprintf(stderr, "Unknown or ambiguous terminal name '%s'\n", term_name);
1682     }
1683     change_term("unknown", 7);
1684 }
1685 
1686 
1687 /* test terminal by drawing border and text */
1688 /* called from command test */
1689 void
test_term()1690 test_term()
1691 {
1692     struct termentry *t = term;
1693     const char *str;
1694     int x, y, xl, yl, i;
1695     int xmax_t, ymax_t, x0, y0;
1696     char label[MAX_ID_LEN];
1697     int key_entry_height;
1698     int p_width;
1699     TBOOLEAN already_in_enhanced_text_mode;
1700     static t_colorspec black = BLACK_COLORSPEC;
1701 
1702     already_in_enhanced_text_mode = t->flags & TERM_ENHANCED_TEXT;
1703     if (!already_in_enhanced_text_mode)
1704 	do_string("set termopt enh");
1705 
1706     term_start_plot();
1707     screen_ok = FALSE;
1708     xmax_t = (t->xmax * xsize);
1709     ymax_t = (t->ymax * ysize);
1710     x0 = (xoffset * t->xmax);
1711     y0 = (yoffset * t->ymax);
1712 
1713     p_width = pointsize * t->h_tic;
1714     key_entry_height = pointsize * t->v_tic * 1.25;
1715     if (key_entry_height < t->v_char)
1716 	key_entry_height = t->v_char;
1717 
1718     /* Sync point for epslatex text positioning */
1719     (*t->layer)(TERM_LAYER_FRONTTEXT);
1720 
1721     /* border linetype */
1722     (*t->linewidth) (1.0);
1723     (*t->linetype) (LT_BLACK);
1724     newpath();
1725     (*t->move) (x0, y0);
1726     (*t->vector) (x0 + xmax_t - 1, y0);
1727     (*t->vector) (x0 + xmax_t - 1, y0 + ymax_t - 1);
1728     (*t->vector) (x0, y0 + ymax_t - 1);
1729     (*t->vector) (x0, y0);
1730     closepath();
1731 
1732     /* Echo back the current terminal type */
1733     if (!strcmp(term->name,"unknown"))
1734 	int_error(NO_CARET, "terminal type is unknown");
1735     else {
1736 	char tbuf[64];
1737 	(void) (*t->justify_text) (LEFT);
1738 	sprintf(tbuf,"%s  terminal test", term->name);
1739 	(*t->put_text) (x0 + t->h_char * 2, y0 + ymax_t - t->v_char, tbuf);
1740 	sprintf(tbuf, "gnuplot version %s.%s  ", gnuplot_version, gnuplot_patchlevel);
1741 	(*t->put_text) (x0 + t->h_char * 2, y0 + ymax_t - t->v_char * 2.25, tbuf);
1742     }
1743 
1744     (*t->linetype) (LT_AXIS);
1745     (*t->move) (x0 + xmax_t / 2, y0);
1746     (*t->vector) (x0 + xmax_t / 2, y0 + ymax_t - 1);
1747     (*t->move) (x0, y0 + ymax_t / 2);
1748     (*t->vector) (x0 + xmax_t - 1, y0 + ymax_t / 2);
1749 
1750     /* How well can we estimate width and height of characters?
1751      * Textbox fill shows true size, surrounding box shows the generic estimate
1752      * used to reserve space during plot layout.
1753      */
1754 #ifdef EAM_BOXED_TEXT
1755     if (TRUE) {
1756 	struct text_label sample = EMPTY_LABELSTRUCT;
1757 	struct textbox_style save_opts = textbox_opts;
1758 	sample.text = "12345678901234567890";
1759 	sample.pos = CENTRE;
1760 	sample.boxed = 1;
1761 	textbox_opts.opaque = TRUE;
1762 	textbox_opts.noborder = TRUE;
1763 	textbox_opts.fillcolor.type = TC_RGB;
1764 	textbox_opts.fillcolor.lt = 0xccccee;
1765 
1766 	(*t->linetype) (LT_SOLID);
1767 	write_label(xmax_t/2, ymax_t/2, &sample);
1768 	textbox_opts = save_opts;
1769 
1770 	sample.boxed = 0;
1771 	sample.text = "true vs. estimated text dimensions";
1772 	write_label(xmax_t/2, ymax_t/2 + 1.5 * t->v_char, &sample);
1773 
1774 	newpath();
1775 	(*t->move) (x0 + xmax_t / 2 - t->h_char * 10, y0 + ymax_t / 2 + t->v_char / 2);
1776 	(*t->vector) (x0 + xmax_t / 2 + t->h_char * 10, y0 + ymax_t / 2 + t->v_char / 2);
1777 	(*t->vector) (x0 + xmax_t / 2 + t->h_char * 10, y0 + ymax_t / 2 - t->v_char / 2);
1778 	(*t->vector) (x0 + xmax_t / 2 - t->h_char * 10, y0 + ymax_t / 2 - t->v_char / 2);
1779 	(*t->vector) (x0 + xmax_t / 2 - t->h_char * 10, y0 + ymax_t / 2 + t->v_char / 2);
1780 	closepath();
1781     }
1782 #endif
1783 
1784     /* Test for enhanced text */
1785     (*t->linetype) (LT_BLACK);
1786     if (t->flags & TERM_ENHANCED_TEXT) {
1787 	char *tmptext1 =   "Enhanced text:   {x@_{0}^{n+1}}";
1788 	char *tmptext2 = "&{Enhanced text:  }{/:Bold Bold}{/:Italic  Italic}";
1789 	(*t->put_text) (x0 + xmax_t * 0.5, y0 + ymax_t * 0.40, tmptext1);
1790 	(*t->put_text) (x0 + xmax_t * 0.5, y0 + ymax_t * 0.35, tmptext2);
1791 	(*t->set_font)("");
1792 	if (!already_in_enhanced_text_mode)
1793 	    do_string("set termopt noenh");
1794     }
1795 
1796     /* test justification */
1797     (void) (*t->justify_text) (LEFT);
1798     (*t->put_text) (x0 + xmax_t / 2, y0 + ymax_t / 2 + t->v_char * 6, "left justified");
1799     str = "centre+d text";
1800     if ((*t->justify_text) (CENTRE))
1801 	(*t->put_text) (x0 + xmax_t / 2,
1802 			y0 + ymax_t / 2 + t->v_char * 5, str);
1803     else
1804 	(*t->put_text) (x0 + xmax_t / 2 - strlen(str) * t->h_char / 2,
1805 			y0 + ymax_t / 2 + t->v_char * 5, str);
1806     str = "right justified";
1807     if ((*t->justify_text) (RIGHT))
1808 	(*t->put_text) (x0 + xmax_t / 2,
1809 			y0 + ymax_t / 2 + t->v_char * 4, str);
1810     else
1811 	(*t->put_text) (x0 + xmax_t / 2 - strlen(str) * t->h_char,
1812 			y0 + ymax_t / 2 + t->v_char * 4, str);
1813 
1814     /* test tic size */
1815     (*t->linetype)(2);
1816     (*t->move) ((unsigned int) (x0 + xmax_t / 2 + t->h_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)), y0 + (unsigned int) ymax_t - 1);
1817     (*t->vector) ((unsigned int) (x0 + xmax_t / 2 + t->h_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)),
1818 		  (unsigned int) (y0 + ymax_t - axis_array[FIRST_X_AXIS].ticscale * t->v_tic));
1819     (*t->move) ((unsigned int) (x0 + xmax_t / 2), y0 + (unsigned int) (ymax_t - t->v_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)));
1820     (*t->vector) ((unsigned int) (x0 + xmax_t / 2 + axis_array[FIRST_X_AXIS].ticscale * t->h_tic),
1821 		  (unsigned int) (y0 + ymax_t - t->v_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)));
1822     (void) (*t->justify_text) (RIGHT);
1823     (*t->put_text) (x0 + (unsigned int) (xmax_t / 2 - 1* t->h_char),
1824 		    y0 + (unsigned int) (ymax_t - t->v_char),
1825 		    "show ticscale");
1826     (void) (*t->justify_text) (LEFT);
1827     (*t->linetype)(LT_BLACK);
1828 
1829     /* test line and point types */
1830     x = x0 + xmax_t - t->h_char * 7 - p_width;
1831     y = y0 + ymax_t - key_entry_height;
1832     (*t->pointsize) (pointsize);
1833     for (i = -2; y > y0 + key_entry_height; i++) {
1834 	struct lp_style_type ls = DEFAULT_LP_STYLE_TYPE;
1835 	ls.l_width = 1;
1836 	load_linetype(&ls,i+1);
1837 	term_apply_lp_properties(&ls);
1838 
1839 	(void) sprintf(label, "%d", i + 1);
1840 	if ((*t->justify_text) (RIGHT))
1841 	    (*t->put_text) (x, y, label);
1842 	else
1843 	    (*t->put_text) (x - strlen(label) * t->h_char, y, label);
1844 	(*t->move) (x + t->h_char, y);
1845 	(*t->vector) (x + t->h_char * 5, y);
1846 	if (i >= -1)
1847 	    (*t->point) (x + t->h_char * 6 + p_width / 2, y, i);
1848 	y -= key_entry_height;
1849     }
1850 
1851     /* test arrows (should line up with rotated text) */
1852     (*t->linewidth) (1.0);
1853     (*t->linetype) (0);
1854     (*t->dashtype) (DASHTYPE_SOLID, NULL);
1855     x = x0 + 2. * t->v_char;
1856     y = y0 + ymax_t/2;
1857     xl = t->h_tic * 7;
1858     yl = t->v_tic * 7;
1859     i = curr_arrow_headfilled;
1860     curr_arrow_headfilled = AS_NOBORDER;
1861     (*t->arrow) (x, y-yl, x, y+yl, BOTH_HEADS);
1862     curr_arrow_headfilled = AS_EMPTY;
1863     (*t->arrow) (x, y, x + xl, y + yl, END_HEAD);
1864     curr_arrow_headfilled = AS_NOFILL;
1865     (*t->arrow) (x, y, x + xl, y - yl, END_HEAD);
1866     curr_arrow_headfilled = i;
1867 
1868     /* test text angle (should match arrows) */
1869     (*t->linetype)(0);
1870     str = "rotated ce+ntred text";
1871     if ((*t->text_angle) (TEXT_VERTICAL)) {
1872 	if ((*t->justify_text) (CENTRE))
1873 	    (*t->put_text) (x0 + t->v_char,
1874 			    y0 + ymax_t / 2, str);
1875 	else
1876 	    (*t->put_text) (x0 + t->v_char,
1877 			    y0 + ymax_t / 2 - strlen(str) * t->h_char / 2, str);
1878 	(*t->justify_text) (LEFT);
1879 	str = "  rotate by +45";
1880 	(*t->text_angle)(45);
1881 	(*t->put_text)(x0 + t->v_char * 3, y0 + ymax_t / 2, str);
1882 	(*t->justify_text) (LEFT);
1883 	str = "  rotate by -45";
1884 	(*t->text_angle)(-45);
1885 	(*t->put_text)(x0 + t->v_char * 3, y0 + ymax_t / 2, str);
1886     } else {
1887 	(void) (*t->justify_text) (LEFT);
1888 	(*t->put_text) (x0 + t->h_char * 2, y0 + ymax_t / 2, "cannot rotate text");
1889     }
1890     (void) (*t->justify_text) (LEFT);
1891     (void) (*t->text_angle) (0);
1892 
1893     /* test line widths */
1894     (void) (*t->justify_text) (LEFT);
1895     xl = xmax_t / 10;
1896     yl = ymax_t / 25;
1897     x = x0 + xmax_t * .075;
1898     y = y0 + yl;
1899 
1900     for (i=1; i<7; i++) {
1901 	(*t->linewidth) ((float)(i)); (*t->linetype)(LT_BLACK);
1902 	(*t->move) (x, y); (*t->vector) (x+xl, y);
1903 	sprintf(label,"  lw %1d", i);
1904 	(*t->put_text) (x+xl, y, label);
1905 	y += yl;
1906     }
1907     (*t->put_text) (x, y, "linewidth");
1908 
1909     /* test native dashtypes (_not_ the 'set mono' sequence) */
1910     (void) (*t->justify_text) (LEFT);
1911     xl = xmax_t / 10;
1912     yl = ymax_t / 25;
1913     x = x0 + xmax_t * .3;
1914     y = y0 + yl;
1915 
1916     for (i=0; i<5; i++) {
1917  	(*t->linewidth) (1.0);
1918 	(*t->linetype) (LT_SOLID);
1919 	(*t->dashtype) (i, NULL);
1920 	(*t->set_color)(&black);
1921 	(*t->move) (x, y); (*t->vector) (x+xl, y);
1922 	sprintf(label,"  dt %1d", i+1);
1923 	(*t->put_text) (x+xl, y, label);
1924 	y += yl;
1925     }
1926     (*t->put_text) (x, y, "dashtype");
1927 
1928     /* test fill patterns */
1929     x = x0 + xmax_t * 0.5;
1930     y = y0;
1931     xl = xmax_t / 40;
1932     yl = ymax_t / 8;
1933     (*t->linewidth) ((float)(1));
1934     (*t->linetype)(LT_BLACK);
1935     (*t->justify_text) (CENTRE);
1936     (*t->put_text)(x+xl*7, y + yl+t->v_char*1.5, "pattern fill");
1937     for (i=0; i<9; i++) {
1938 	int style = ((i<<4) + FS_PATTERN);
1939 	if (t->fillbox)
1940 	    (*t->fillbox) ( style, x, y, xl, yl );
1941 	newpath();
1942 	(*t->move)  (x,y);
1943 	(*t->vector)(x,y+yl);
1944 	(*t->vector)(x+xl,y+yl);
1945 	(*t->vector)(x+xl,y);
1946 	(*t->vector)(x,y);
1947 	closepath();
1948 	sprintf(label, "%2d", i);
1949 	(*t->put_text)(x+xl/2, y+yl+t->v_char*0.5, label);
1950 	x += xl * 1.5;
1951     }
1952 
1953     {
1954 	int cen_x = x0 + (int)(0.70 * xmax_t);
1955 	int cen_y = y0 + (int)(0.83 * ymax_t);
1956 	int radius = xmax_t / 20;
1957 
1958 	/* test pm3d -- filled_polygon(), but not set_color() */
1959 	if (t->filled_polygon) {
1960 	    int i, j;
1961 #define NUMBER_OF_VERTICES 6
1962 	    int n = NUMBER_OF_VERTICES;
1963 	    gpiPoint corners[NUMBER_OF_VERTICES+1];
1964 #undef  NUMBER_OF_VERTICES
1965 
1966 	    for (j=0; j<=1; j++) {
1967 		int ix = cen_x + j*radius;
1968 		int iy = cen_y - j*radius/2;
1969 		for (i = 0; i < n; i++) {
1970 		    corners[i].x = ix + radius * cos(2*M_PI*i/n);
1971 		    corners[i].y = iy + radius * sin(2*M_PI*i/n);
1972 		}
1973 		corners[n].x = corners[0].x;
1974 		corners[n].y = corners[0].y;
1975 		if (j == 0) {
1976 		    (*t->linetype)(2);
1977 		    corners->style = FS_OPAQUE;
1978 		} else {
1979 		    (*t->linetype)(1);
1980 		    corners->style = FS_TRANSPARENT_SOLID + (50<<4);
1981 		}
1982 		term->filled_polygon(n+1, corners);
1983 	    }
1984 	    str = "filled polygons:";
1985 	} else
1986 	    str = "No filled polygons";
1987 	(*t->linetype)(LT_BLACK);
1988 	i = ((*t->justify_text) (CENTRE)) ? 0 : t->h_char * strlen(str) / 2;
1989 	(*t->put_text) (cen_x - i, cen_y + radius + t->v_char * 0.5, str);
1990     }
1991 
1992     term_end_plot();
1993 }
1994 
1995 
1996 #ifdef VMS
1997 /* these are needed to modify terminal characteristics */
1998 # ifndef VWS_XMAX
1999    /* avoid duplicate warning; VWS includes these */
2000 #  include <descrip.h>
2001 #  include <ssdef.h>
2002 # endif                         /* !VWS_MAX */
2003 # include <iodef.h>
2004 # include <ttdef.h>
2005 # include <tt2def.h>
2006 # include <dcdef.h>
2007 # include <stat.h>
2008 # include <fab.h>
2009 /* If you use WATCOM C or a very strict ANSI compiler, you may have to
2010  * delete or comment out the following 3 lines: */
2011 # ifndef TT2$M_DECCRT3          /* VT300 not defined as of VAXC v2.4 */
2012 #  define TT2$M_DECCRT3 0X80000000
2013 # endif
2014 static unsigned short chan;
2015 static int old_char_buf[3], cur_char_buf[3];
2016 $DESCRIPTOR(sysoutput_desc, "SYS$OUTPUT");
2017 
2018 /* Look first for decw$display (decterms do regis).  Determine if we
2019  * have a regis terminal and save terminal characteristics */
2020 char *
vms_init()2021 vms_init()
2022 {
2023     /* Save terminal characteristics in old_char_buf and
2024        initialise cur_char_buf to current settings. */
2025     int i;
2026 #ifdef X11
2027     if (getenv("DECW$DISPLAY"))
2028 	return ("x11");
2029 #endif
2030     atexit(vms_reset);
2031     sys$assign(&sysoutput_desc, &chan, 0, 0);
2032     sys$qiow(0, chan, IO$_SENSEMODE, 0, 0, 0, old_char_buf, 12, 0, 0, 0, 0);
2033     for (i = 0; i < 3; ++i)
2034 	cur_char_buf[i] = old_char_buf[i];
2035     sys$dassgn(chan);
2036 
2037     /* Test if terminal is regis */
2038     if ((cur_char_buf[2] & TT2$M_REGIS) == TT2$M_REGIS)
2039 	return ("regis");
2040     return (NULL);
2041 }
2042 
2043 /* set terminal to original state */
2044 void
vms_reset()2045 vms_reset()
2046 {
2047     int i;
2048 
2049     sys$assign(&sysoutput_desc, &chan, 0, 0);
2050     sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, old_char_buf, 12, 0, 0, 0, 0);
2051     for (i = 0; i < 3; ++i)
2052 	cur_char_buf[i] = old_char_buf[i];
2053     sys$dassgn(chan);
2054 }
2055 
2056 /* set terminal mode to tektronix */
2057 void
term_mode_tek()2058 term_mode_tek()
2059 {
2060     long status;
2061 
2062     if (gpoutfile != stdout)
2063 	return;                 /* don't modify if not stdout */
2064     sys$assign(&sysoutput_desc, &chan, 0, 0);
2065     cur_char_buf[0] = 0x004A0000 | DC$_TERM | (TT$_TEK401X << 8);
2066     cur_char_buf[1] = (cur_char_buf[1] & 0x00FFFFFF) | 0x18000000;
2067 
2068     cur_char_buf[1] &= ~TT$M_CRFILL;
2069     cur_char_buf[1] &= ~TT$M_ESCAPE;
2070     cur_char_buf[1] &= ~TT$M_HALFDUP;
2071     cur_char_buf[1] &= ~TT$M_LFFILL;
2072     cur_char_buf[1] &= ~TT$M_MECHFORM;
2073     cur_char_buf[1] &= ~TT$M_NOBRDCST;
2074     cur_char_buf[1] &= ~TT$M_NOECHO;
2075     cur_char_buf[1] &= ~TT$M_READSYNC;
2076     cur_char_buf[1] &= ~TT$M_REMOTE;
2077     cur_char_buf[1] |= TT$M_LOWER;
2078     cur_char_buf[1] |= TT$M_TTSYNC;
2079     cur_char_buf[1] |= TT$M_WRAP;
2080     cur_char_buf[1] &= ~TT$M_EIGHTBIT;
2081     cur_char_buf[1] &= ~TT$M_MECHTAB;
2082     cur_char_buf[1] &= ~TT$M_SCOPE;
2083     cur_char_buf[1] |= TT$M_HOSTSYNC;
2084 
2085     cur_char_buf[2] &= ~TT2$M_APP_KEYPAD;
2086     cur_char_buf[2] &= ~TT2$M_BLOCK;
2087     cur_char_buf[2] &= ~TT2$M_DECCRT3;
2088     cur_char_buf[2] &= ~TT2$M_LOCALECHO;
2089     cur_char_buf[2] &= ~TT2$M_PASTHRU;
2090     cur_char_buf[2] &= ~TT2$M_REGIS;
2091     cur_char_buf[2] &= ~TT2$M_SIXEL;
2092     cur_char_buf[2] |= TT2$M_BRDCSTMBX;
2093     cur_char_buf[2] |= TT2$M_EDITING;
2094     cur_char_buf[2] |= TT2$M_INSERT;
2095     cur_char_buf[2] |= TT2$M_PRINTER;
2096     cur_char_buf[2] &= ~TT2$M_ANSICRT;
2097     cur_char_buf[2] &= ~TT2$M_AVO;
2098     cur_char_buf[2] &= ~TT2$M_DECCRT;
2099     cur_char_buf[2] &= ~TT2$M_DECCRT2;
2100     cur_char_buf[2] &= ~TT2$M_DRCS;
2101     cur_char_buf[2] &= ~TT2$M_EDIT;
2102     cur_char_buf[2] |= TT2$M_FALLBACK;
2103 
2104     status = sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, cur_char_buf, 12, 0, 0, 0, 0);
2105     if (status == SS$_BADPARAM) {
2106 	/* terminal fallback utility not installed on system */
2107 	cur_char_buf[2] &= ~TT2$M_FALLBACK;
2108 	sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, cur_char_buf, 12, 0, 0, 0, 0);
2109     } else {
2110 	if (status != SS$_NORMAL)
2111 	    lib$signal(status, 0, 0);
2112     }
2113     sys$dassgn(chan);
2114 }
2115 
2116 /* set terminal mode back to native */
2117 void
term_mode_native()2118 term_mode_native()
2119 {
2120     int i;
2121 
2122     if (gpoutfile != stdout)
2123 	return;                 /* don't modify if not stdout */
2124     sys$assign(&sysoutput_desc, &chan, 0, 0);
2125     sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, old_char_buf, 12, 0, 0, 0, 0);
2126     for (i = 0; i < 3; ++i)
2127 	cur_char_buf[i] = old_char_buf[i];
2128     sys$dassgn(chan);
2129 }
2130 
2131 /* set terminal mode pasthru */
2132 void
term_pasthru()2133 term_pasthru()
2134 {
2135     if (gpoutfile != stdout)
2136 	return;                 /* don't modify if not stdout */
2137     sys$assign(&sysoutput_desc, &chan, 0, 0);
2138     cur_char_buf[2] |= TT2$M_PASTHRU;
2139     sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, cur_char_buf, 12, 0, 0, 0, 0);
2140     sys$dassgn(chan);
2141 }
2142 
2143 /* set terminal mode nopasthru */
2144 void
term_nopasthru()2145 term_nopasthru()
2146 {
2147     if (gpoutfile != stdout)
2148 	return;                 /* don't modify if not stdout */
2149     sys$assign(&sysoutput_desc, &chan, 0, 0);
2150     cur_char_buf[2] &= ~TT2$M_PASTHRU;
2151     sys$qiow(0, chan, IO$_SETMODE, 0, 0, 0, cur_char_buf, 12, 0, 0, 0, 0);
2152     sys$dassgn(chan);
2153 }
2154 
2155 void
fflush_binary()2156 fflush_binary()
2157 {
2158     typedef short int INT16;    /* signed 16-bit integers */
2159     INT16 k;            /* loop index */
2160 
2161     if (gpoutfile != stdout) {
2162 	/* Stupid VMS fflush() raises error and loses last data block
2163 	   unless it is full for a fixed-length record binary file.
2164 	   Pad it here with NULL characters. */
2165 	for (k = (INT16) ((*gpoutfile)->_cnt); k > 0; --k)
2166 	    putc('\0', gpoutfile);
2167 	fflush(gpoutfile);
2168     }
2169 }
2170 #endif /* VMS */
2171 
2172 /*
2173  * This is an abstraction of the enhanced text mode originally written
2174  * for the postscript terminal driver by David Denholm and Matt Heffron.
2175  * I have split out a terminal-independent recursive syntax-parser
2176  * routine that can be shared by all drivers that want to add support
2177  * for enhanced text mode.
2178  *
2179  * A driver that wants to make use of this common framework must provide
2180  * three new entries in TERM_TABLE:
2181  *      void *enhanced_open   (char *fontname, double fontsize, double base,
2182  *                             TBOOLEAN widthflag, TBOOLEAN showflag,
2183  *                             int overprint)
2184  *      void *enhanced_writec (char c)
2185  *      void *enhanced_flush  ()
2186  *
2187  * Each driver also has a separate ENHXX_put_text() routine that replaces
2188  * the normal (term->put_text) routine while in enhanced mode.
2189  * This routine must initialize the following globals used by the shared code:
2190  *      enhanced_fontscale      converts font size to device resolution units
2191  *      enhanced_escape_format  used to process octal escape characters \xyz
2192  *
2193  * I bent over backwards to make the output of the revised code identical
2194  * to the output of the original postscript version.  That means there is
2195  * some cruft left in here (enhanced_max_height for one thing) that is
2196  * probably irrelevant to any new drivers using the code.
2197  *
2198  * Ethan A Merritt - November 2003
2199  */
2200 
2201 #ifdef DEBUG_ENH
2202 #define ENH_DEBUG(x) printf x;
2203 #else
2204 #define ENH_DEBUG(x)
2205 #endif
2206 
2207 void
do_enh_writec(int c)2208 do_enh_writec(int c)
2209 {
2210     /* note: c is meant to hold a char, but is actually an int, for
2211      * the same reasons applying to putc() and friends */
2212     *enhanced_cur_text++ = c;
2213 }
2214 
2215 /*
2216  * Process a bit of string, and return the last character used.
2217  * p is start of string
2218  * brace is TRUE to keep processing to }, FALSE to do one character only
2219  * fontname & fontsize are obvious
2220  * base is the current baseline
2221  * widthflag is TRUE if the width of this should count,
2222  *              FALSE for zero width boxes
2223  * showflag is TRUE if this should be shown,
2224  *             FALSE if it should not be shown (like TeX \phantom)
2225  * overprint is 0 for normal operation,
2226  *              1 for the underprinted text (included in width calculation),
2227  *              2 for the overprinted text (not included in width calc)
2228  *              (overprinted text is centered horizontally on underprinted text
2229  */
2230 
2231 const char *
enhanced_recursion(const char * p,TBOOLEAN brace,char * fontname,double fontsize,double base,TBOOLEAN widthflag,TBOOLEAN showflag,int overprint)2232 enhanced_recursion(
2233     const char *p,
2234     TBOOLEAN brace,
2235     char *fontname,
2236     double fontsize,
2237     double base,
2238     TBOOLEAN widthflag,
2239     TBOOLEAN showflag,
2240     int overprint)
2241 {
2242     TBOOLEAN wasitalic, wasbold;
2243 
2244     /* Keep track of the style of the font passed in at this recursion level */
2245     wasitalic = (strstr(fontname, ":Italic") != NULL);
2246     wasbold = (strstr(fontname, ":Bold") != NULL);
2247 
2248     FPRINTF((stderr, "RECURSE WITH \"%s\", %d %s %.1f %.1f %d %d %d",
2249 		p, brace, fontname, fontsize, base, widthflag, showflag, overprint));
2250 
2251     /* Start each recursion with a clean string */
2252     (term->enhanced_flush)();
2253 
2254     if (base + fontsize > enhanced_max_height) {
2255 	enhanced_max_height = base + fontsize;
2256 	ENH_DEBUG(("Setting max height to %.1f\n", enhanced_max_height));
2257     }
2258 
2259     if (base < enhanced_min_height) {
2260 	enhanced_min_height = base;
2261 	ENH_DEBUG(("Setting min height to %.1f\n", enhanced_min_height));
2262     }
2263 
2264     while (*p) {
2265 	float shift;
2266 
2267 	/*
2268 	 * EAM Jun 2009 - treating bytes one at a time does not work for multibyte
2269 	 * encodings, including utf-8. If we hit a byte with the high bit set, test
2270 	 * whether it starts a legal UTF-8 sequence and if so copy the whole thing.
2271 	 * Other multibyte encodings are still a problem.
2272 	 * Gnuplot's other defined encodings are all single-byte; for those we
2273 	 * really do want to treat one byte at a time.
2274 	 */
2275 	if ((*p & 0x80) && (encoding == S_ENC_DEFAULT || encoding == S_ENC_UTF8)) {
2276 	    unsigned long utf8char;
2277 	    const char *nextchar = p;
2278 
2279 	    (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2280 	    if (utf8toulong(&utf8char, &nextchar)) {	/* Legal UTF8 sequence */
2281 		while (p < nextchar)
2282 		    (term->enhanced_writec)(*p++);
2283 		p--;
2284 	    } else {					/* Some other multibyte encoding? */
2285 		(term->enhanced_writec)(*p);
2286 	    }
2287 /* shige : for Shift_JIS */
2288 	} else if ((*p & 0x80) && (encoding == S_ENC_SJIS)) {
2289 	    (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2290 	    (term->enhanced_writec)(*(p++));
2291 	    (term->enhanced_writec)(*p);
2292 	} else
2293 
2294 	switch (*p) {
2295 	case '}'  :
2296 	    /*{{{  deal with it*/
2297 	    if (brace)
2298 		return (p);
2299 
2300 	    int_warn(NO_CARET, "enhanced text parser - spurious }");
2301 	    break;
2302 	    /*}}}*/
2303 
2304 	case '_'  :
2305 	case '^'  :
2306 	    /*{{{  deal with super/sub script*/
2307 	    shift = (*p == '^') ? 0.5 : -0.3;
2308 	    (term->enhanced_flush)();
2309 	    p = enhanced_recursion(p + 1, FALSE, fontname, fontsize * 0.8,
2310 			      base + shift * fontsize, widthflag,
2311 			      showflag, overprint);
2312 	    break;
2313 	    /*}}}*/
2314 	case '{'  :
2315 	    {
2316 		TBOOLEAN isitalic = FALSE, isbold = FALSE, isnormal = FALSE;
2317 		const char *start_of_fontname = NULL;
2318 		const char *end_of_fontname = NULL;
2319 		char *localfontname = NULL;
2320 		char ch;
2321 		float f = fontsize, ovp;
2322 
2323 		/* Mar 2014 - this will hold "fontfamily{:Italic}{:Bold}" */
2324 		char *styledfontname = NULL;
2325 
2326 		/*{{{  recurse (possibly with a new font) */
2327 
2328 		ENH_DEBUG(("Dealing with {\n"));
2329 
2330 		/* 30 Sep 2016:  Remove incorrect whitespace-eating loop going */
2331 		/* waaay back to 31-May-2000 */        /* while (*++p == ' '); */
2332 		++p;
2333 		/* get vertical offset (if present) for overprinted text */
2334 		if (overprint == 2) {
2335 		    char *end;
2336 		    ovp = (float)strtod(p,&end);
2337 		    p = end;
2338 		    if (term->flags & TERM_IS_POSTSCRIPT)
2339 			base = ovp*f;
2340 		    else
2341 			base += ovp*f;
2342 		}
2343 		--p;
2344 
2345 		if (*++p == '/') {
2346 		    /* then parse a fontname, optional fontsize */
2347 		    while (*++p == ' ')
2348 			;       /* do nothing */
2349 		    if (*p=='-') {
2350 			while (*++p == ' ')
2351 			    ;   /* do nothing */
2352 		    }
2353 		    start_of_fontname = p;
2354 
2355 		    /* Allow font name to be in quotes.
2356 		     * This makes it possible to handle font names containing spaces.
2357 		     */
2358 		    if (*p == '\'' || *p == '"') {
2359 			++p;
2360 			while (*p != '\0' && *p != '}' && *p != *start_of_fontname)
2361 			    ++p;
2362 			if (*p != *start_of_fontname) {
2363 			    int_warn(NO_CARET, "cannot interpret font name %s", start_of_fontname);
2364 			    p = start_of_fontname;
2365 			}
2366 			start_of_fontname++;
2367 			end_of_fontname = p++;
2368 			ch = *p;
2369 		    } else {
2370 
2371 		    /* Normal unquoted font name */
2372 			while ((ch = *p) > ' ' && ch != '=' && ch != '*' && ch != '}' && ch != ':')
2373 			    ++p;
2374 			end_of_fontname = p;
2375 		    }
2376 
2377 		    do {
2378 			if (ch == '=') {
2379 			    /* get optional font size */
2380 			    char *end;
2381 			    p++;
2382 			    ENH_DEBUG(("Calling strtod(\"%s\") ...", p));
2383 			    f = (float)strtod(p, &end);
2384 			    p = end;
2385 			    ENH_DEBUG(("Returned %.1f and \"%s\"\n", f, p));
2386 
2387 			    if (f == 0)
2388 				f = fontsize;
2389 			    else
2390 				f *= enhanced_fontscale;  /* remember the scaling */
2391 
2392 			    ENH_DEBUG(("Font size %.1f\n", f));
2393 			} else if (ch == '*') {
2394 			    /* get optional font size scale factor */
2395 			    char *end;
2396 			    p++;
2397 			    ENH_DEBUG(("Calling strtod(\"%s\") ...", p));
2398 			    f = (float)strtod(p, &end);
2399 			    p = end;
2400 			    ENH_DEBUG(("Returned %.1f and \"%s\"\n", f, p));
2401 
2402 			    if (f)
2403 				f *= fontsize;  /* apply the scale factor */
2404 			    else
2405 				f = fontsize;
2406 
2407 			    ENH_DEBUG(("Font size %.1f\n", f));
2408 			} else if (ch == ':') {
2409 			    /* get optional style markup attributes */
2410 			    p++;
2411 			    if (!strncmp(p,"Bold",4))
2412 				isbold = TRUE;
2413 			    if (!strncmp(p,"Italic",6))
2414 				isitalic = TRUE;
2415 			    if (!strncmp(p,"Normal",6))
2416 				isnormal = TRUE;
2417 			    while (isalpha((unsigned char)*p)) {p++;}
2418 			}
2419 		    } while (((ch = *p) == '=') || (ch == ':') || (ch == '*'));
2420 
2421 		    if (ch == '}')
2422 			int_warn(NO_CARET,"bad syntax in enhanced text string");
2423 
2424 		    if (*p == ' ')	/* Eat up a single space following a font spec */
2425 			++p;
2426 		    if (!start_of_fontname || (start_of_fontname == end_of_fontname)) {
2427 			/* Use the font name passed in to us */
2428 			localfontname = gp_strdup(fontname);
2429 		    } else {
2430 			/* We found a new font name {/Font ...} */
2431 			int len = end_of_fontname - start_of_fontname;
2432 			localfontname = gp_alloc(len+1,"localfontname");
2433 			strncpy(localfontname, start_of_fontname, len);
2434 			localfontname[len] = '\0';
2435 		    }
2436 		}
2437 		/*}}}*/
2438 
2439 		/* Collect cumulative style markup before passing it in the font name */
2440 		isitalic = (wasitalic || isitalic) && !isnormal;
2441 		isbold = (wasbold || isbold) && !isnormal;
2442 
2443 		styledfontname = stylefont(localfontname ? localfontname : fontname,
2444 					    isbold, isitalic);
2445 
2446 		p = enhanced_recursion(p, TRUE, styledfontname, f, base,
2447 				  widthflag, showflag, overprint);
2448 
2449 		(term->enhanced_flush)();
2450 
2451 		free(styledfontname);
2452 		free(localfontname);
2453 
2454 		break;
2455 	    } /* case '{' */
2456 	case '@' :
2457 	    /*{{{  phantom box - prints next 'char', then restores currentpoint */
2458 	    (term->enhanced_flush)();
2459 	    (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, 3);
2460 	    p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
2461 			      widthflag, showflag, overprint);
2462 	    (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, 4);
2463 	    break;
2464 	    /*}}}*/
2465 
2466 	case '&' :
2467 	    /*{{{  character skip - skips space equal to length of character(s) */
2468 	    (term->enhanced_flush)();
2469 
2470 	    p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
2471 			      widthflag, FALSE, overprint);
2472 	    break;
2473 	    /*}}}*/
2474 
2475 	case '~' :
2476 	    /*{{{ overprinted text */
2477 	    /* the second string is overwritten on the first, centered
2478 	     * horizontally on the first and (optionally) vertically
2479 	     * shifted by an amount specified (as a fraction of the
2480 	     * current fontsize) at the beginning of the second string
2481 
2482 	     * Note that in this implementation neither the under- nor
2483 	     * overprinted string can contain syntax that would result
2484 	     * in additional recursions -- no subscripts,
2485 	     * superscripts, or anything else, with the exception of a
2486 	     * font definition at the beginning of the text */
2487 
2488 	    (term->enhanced_flush)();
2489 	    p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
2490 			      widthflag, showflag, 1);
2491 	    (term->enhanced_flush)();
2492 	    if (!*p)
2493 	        break;
2494 	    p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
2495 			      FALSE, showflag, 2);
2496 
2497 	    overprint = 0;   /* may not be necessary, but just in case . . . */
2498 	    break;
2499 	    /*}}}*/
2500 
2501 	case '('  :
2502 	case ')'  :
2503 	    /*{{{  an escape and print it */
2504 	    /* special cases */
2505 	    (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2506 	    if (term->flags & TERM_IS_POSTSCRIPT)
2507 		(term->enhanced_writec)('\\');
2508 	    (term->enhanced_writec)(*p);
2509 	    break;
2510 	    /*}}}*/
2511 
2512 	case '\\'  :
2513 	    /*{{{  Enhanced mode always uses \xyz as an octal character representation
2514 		   but each terminal type must give us the actual output format wanted.
2515 		   pdf.trm wants the raw character code, which is why we use strtol();
2516 		   most other terminal types want some variant of "\\%o". */
2517 	    if (p[1] >= '0' && p[1] <= '7') {
2518 		char *e, escape[16], octal[4] = {'\0','\0','\0','\0'};
2519 
2520 		(term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2521 		octal[0] = *(++p);
2522 		if (p[1] >= '0' && p[1] <= '7') {
2523 		    octal[1] = *(++p);
2524 		    if (p[1] >= '0' && p[1] <= '7')
2525 			octal[2] = *(++p);
2526 		}
2527 		sprintf(escape, enhanced_escape_format, strtol(octal,NULL,8));
2528 		for (e=escape; *e; e++) {
2529 		    (term->enhanced_writec)(*e);
2530 		}
2531 		break;
2532 	    /* This was the original (prior to version 4) enhanced text code specific */
2533 	    /* to the reserved characters of PostScript.  Some of it was mis-applied  */
2534 	    /* to other terminal types until fixed in Mar 2012.                       */
2535 	    } else if (term->flags & TERM_IS_POSTSCRIPT) {
2536 		if (p[1]=='\\' || p[1]=='(' || p[1]==')') {
2537 		    (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2538 		    (term->enhanced_writec)('\\');
2539 		} else if (strchr("^_@&~{}",p[1]) == NULL) {
2540 		    (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2541 		    (term->enhanced_writec)('\\');
2542 		    (term->enhanced_writec)('\\');
2543 		    break;
2544 		}
2545 	    }
2546 	    ++p;
2547 
2548 	    /* HBB 20030122: Avoid broken output if there's a \
2549 	     * exactly at the end of the line */
2550 	    if (*p == '\0') {
2551 		int_warn(NO_CARET, "enhanced text parser -- spurious backslash");
2552 		break;
2553 	    }
2554 
2555 	    /* SVG requires an escaped '&' to be passed as something else */
2556 	    /* FIXME: terminal-dependent code does not belong here */
2557 	    if (*p == '&' && encoding == S_ENC_DEFAULT && !strcmp(term->name, "svg")) {
2558 		(term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2559 		(term->enhanced_writec)('\376');
2560 		break;
2561 	    }
2562 
2563 	    /* just go and print it (fall into the 'default' case) */
2564 	    /*}}}*/
2565 	default:
2566 	    /*{{{  print it */
2567 	    (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
2568 	    (term->enhanced_writec)(*p);
2569 	    /*}}}*/
2570 	} /* switch (*p) */
2571 
2572 	/* like TeX, we only do one character in a recursion, unless it's
2573 	 * in braces
2574 	 */
2575 
2576 	if (!brace) {
2577 	    (term->enhanced_flush)();
2578 	    return(p);  /* the ++p in the outer copy will increment us */
2579 	}
2580 
2581 	if (*p) /* only not true if { not terminated, I think */
2582 	    ++p;
2583     } /* while (*p) */
2584 
2585     (term->enhanced_flush)();
2586     return p;
2587 }
2588 
2589 /* Strip off anything trailing the requested font name,
2590  * then add back markup requests.
2591  */
2592 char *
stylefont(const char * fontname,TBOOLEAN isbold,TBOOLEAN isitalic)2593 stylefont(const char *fontname, TBOOLEAN isbold, TBOOLEAN isitalic)
2594 {
2595     char *div;
2596     char *markup = gp_alloc( strlen(fontname) + 16, "font markup");
2597     strcpy(markup, fontname);
2598     if ((div = strchr(markup,':')))
2599 	*div = '\0';
2600     if (isbold)
2601 	strcat(markup, ":Bold");
2602     if (isitalic)
2603 	strcat(markup, ":Italic");
2604 
2605     FPRINTF((stderr, "MARKUP FONT: %s -> %s\n", fontname, markup));
2606     return markup;
2607 }
2608 
2609 /* Called after the end of recursion to check for errors */
2610 void
enh_err_check(const char * str)2611 enh_err_check(const char *str)
2612 {
2613     if (*str == '}')
2614 	int_warn(NO_CARET, "enhanced text mode parser - ignoring spurious }");
2615     else
2616 	int_warn(NO_CARET, "enhanced text mode parsing error");
2617 }
2618 
2619 /*
2620  * Text strings containing control information for enhanced text mode
2621  * contain more characters than will actually appear in the output.
2622  * This makes it hard to estimate how much horizontal space on the plot
2623  * (e.g. in the key box) must be reserved to hold them.  To approximate
2624  * the eventual length we switch briefly to the dummy terminal driver
2625  * "estimate.trm" and then switch back to the current terminal.
2626  * If better, perhaps terminal-specific methods of estimation are
2627  * developed later they can be slotted into this one call site.
2628  */
2629 int
estimate_strlen(const char * text)2630 estimate_strlen(const char *text)
2631 {
2632 int len;
2633 
2634     if ((term->flags & TERM_IS_LATEX))
2635 	len = strlen_tex(text);
2636     else
2637 
2638 #ifdef GP_ENH_EST
2639     if (strchr(text,'\n') || (term->flags & TERM_ENHANCED_TEXT)) {
2640 	struct termentry *tsave = term;
2641 	term = &ENHest;
2642 	term->put_text(0,0,text);
2643 	len = term->xmax;
2644 	FPRINTF((stderr,"Estimating length %d height %g for enhanced text string \"%s\"\n",
2645 		len, (double)(term->ymax)/10., text));
2646 	term = tsave;
2647     } else if (encoding == S_ENC_UTF8)
2648 	len = strwidth_utf8(text);
2649     else
2650 #endif
2651 	len = strlen(text);
2652 
2653     return len;
2654 }
2655 
2656 /*
2657  * Use estimate.trm to mock up a non-enhanced approximation of the
2658  * original string.
2659  */
2660 char *
estimate_plaintext(char * enhancedtext)2661 estimate_plaintext(char *enhancedtext)
2662 {
2663     if (enhancedtext == NULL)
2664 	return NULL;
2665     estimate_strlen(enhancedtext);
2666     return ENHest_plaintext;
2667 }
2668 
2669 void
ignore_enhanced(TBOOLEAN flag)2670 ignore_enhanced(TBOOLEAN flag)
2671 {
2672 #if (0)
2673     /* Apr 2018: This code was introduced long ago (2005; Bug #266) to address
2674      * a glitch in the postscript terminal that left the last-used font in
2675      * an enhanced text string active afterwards.
2676      * We now deal with this in ENHPS_put_text() instead.
2677      */
2678     if (flag && !ignore_enhanced_text) {
2679 	ignore_enhanced_text = TRUE;
2680 	term->set_font("");
2681     }
2682 #endif
2683     ignore_enhanced_text = flag;
2684 }
2685 
2686 /* Simple-minded test for whether the point (x,y) is in bounds for the current terminal.
2687  * Some terminals can do their own clipping, and can clip partial objects.
2688  * If the flag TERM_CAN_CLIP is set, we skip this relative crude test and let the
2689  * driver or the hardware handle clipping.
2690  */
2691 TBOOLEAN
on_page(int x,int y)2692 on_page(int x, int y)
2693 {
2694     if (term->flags & TERM_CAN_CLIP)
2695 	return TRUE;
2696 
2697     if ((0 < x && x < term->xmax) && (0 < y && y < term->ymax))
2698 	return TRUE;
2699 
2700     return FALSE;
2701 }
2702 
2703 /* Utility routine for drivers to accept an explicit size for the
2704  * output image.
2705  */
2706 size_units
parse_term_size(float * xsize,float * ysize,size_units default_units)2707 parse_term_size( float *xsize, float *ysize, size_units default_units )
2708 {
2709     size_units units = default_units;
2710 
2711     if (END_OF_COMMAND)
2712 	int_error(c_token, "size requires two numbers:  xsize, ysize");
2713     *xsize = real_expression();
2714     if (almost_equals(c_token,"in$ches")) {
2715 	c_token++;
2716 	units = INCHES;
2717     } else if (equals(c_token,"cm")) {
2718 	c_token++;
2719 	units = CM;
2720     }
2721     switch (units) {
2722     case INCHES:	*xsize *= gp_resolution; break;
2723     case CM:		*xsize *= (float)gp_resolution / 2.54; break;
2724     case PIXELS:
2725     default:		 break;
2726     }
2727 
2728     if (!equals(c_token++,","))
2729 	int_error(c_token, "size requires two numbers:  xsize, ysize");
2730     *ysize = real_expression();
2731     if (almost_equals(c_token,"in$ches")) {
2732 	c_token++;
2733 	units = INCHES;
2734     } else if (equals(c_token,"cm")) {
2735 	c_token++;
2736 	units = CM;
2737     }
2738     switch (units) {
2739     case INCHES:	*ysize *= gp_resolution; break;
2740     case CM:		*ysize *= (float)gp_resolution / 2.54; break;
2741     case PIXELS:
2742     default:		 break;
2743     }
2744 
2745     if (*xsize < 1 || *ysize < 1)
2746 	int_error(c_token, "size: out of range");
2747 
2748     return units;
2749 }
2750 
2751 /*
2752  * Wrappers for newpath and closepath
2753  */
2754 
2755 void
newpath()2756 newpath()
2757 {
2758     if (term->path)
2759 	(*term->path)(0);
2760 }
2761 
2762 void
closepath()2763 closepath()
2764 {
2765     if (term->path)
2766 	(*term->path)(1);
2767 }
2768 
2769 /* Squeeze all fill information into the old style parameter.
2770  * The terminal drivers know how to extract the information.
2771  * We assume that the style (int) has only 16 bit, therefore we take
2772  * 4 bits for the style and allow 12 bits for the corresponding fill parameter.
2773  * This limits the number of styles to 16 and the fill parameter's
2774  * values to the range 0...4095, which seems acceptable.
2775  */
2776 int
style_from_fill(struct fill_style_type * fs)2777 style_from_fill(struct fill_style_type *fs)
2778 {
2779     int fillpar, style;
2780 
2781     switch( fs->fillstyle ) {
2782     case FS_SOLID:
2783     case FS_TRANSPARENT_SOLID:
2784 	fillpar = fs->filldensity;
2785 	style = ((fillpar & 0xfff) << 4) + fs->fillstyle;
2786 	break;
2787     case FS_PATTERN:
2788     case FS_TRANSPARENT_PATTERN:
2789 	fillpar = fs->fillpattern;
2790 	style = ((fillpar & 0xfff) << 4) + fs->fillstyle;
2791 	break;
2792     case FS_EMPTY:
2793     default:
2794 	/* solid fill with background color */
2795 	style = FS_EMPTY;
2796 	break;
2797     }
2798 
2799     return style;
2800 }
2801 
2802 
2803 /*
2804  * Load dt with the properties of a user-defined dashtype.
2805  * Return: DASHTYPE_SOLID or DASHTYPE_CUSTOM or a positive number
2806  * if no user-defined dashtype was found.
2807  */
2808 int
load_dashtype(struct t_dashtype * dt,int tag)2809 load_dashtype(struct t_dashtype *dt, int tag)
2810 {
2811     struct custom_dashtype_def *this;
2812     struct t_dashtype loc_dt = DEFAULT_DASHPATTERN;
2813 
2814     this = first_custom_dashtype;
2815     while (this != NULL) {
2816 	if (this->tag == tag) {
2817 	    *dt = this->dashtype;
2818 	    memcpy(dt->dstring, this->dashtype.dstring, sizeof(dt->dstring));
2819 	    return this->d_type;
2820 	} else {
2821 	    this = this->next;
2822 	}
2823     }
2824 
2825     /* not found, fall back to default, terminal-dependent dashtype */
2826     *dt = loc_dt;
2827     return tag - 1;
2828 }
2829 
2830 
2831 void
lp_use_properties(struct lp_style_type * lp,int tag)2832 lp_use_properties(struct lp_style_type *lp, int tag)
2833 {
2834     /*  This function looks for a linestyle defined by 'tag' and copies
2835      *  its data into the structure 'lp'.
2836      */
2837 
2838     struct linestyle_def *this;
2839     int save_flags = lp->flags;
2840 
2841     this = first_linestyle;
2842     while (this != NULL) {
2843 	if (this->tag == tag) {
2844 	    *lp = this->lp_properties;
2845 	    lp->flags = save_flags;
2846 	    return;
2847 	} else {
2848 	    this = this->next;
2849 	}
2850     }
2851 
2852     /* No user-defined style with this tag; fall back to default line type. */
2853     load_linetype(lp, tag);
2854 }
2855 
2856 
2857 /*
2858  * Load lp with the properties of a user-defined linetype
2859  */
2860 void
load_linetype(struct lp_style_type * lp,int tag)2861 load_linetype(struct lp_style_type *lp, int tag)
2862 {
2863     struct linestyle_def *this;
2864     TBOOLEAN recycled = FALSE;
2865 
2866 recycle:
2867 
2868     if ((tag > 0)
2869     && (monochrome || (term && (term->flags & TERM_MONOCHROME)))) {
2870 	for (this = first_mono_linestyle; this; this = this->next) {
2871 	    if (tag == this->tag) {
2872 		*lp = this->lp_properties;
2873 		return;
2874 	    }
2875 	}
2876 
2877 	/* This linetype wasn't defined explicitly.		*/
2878 	/* Should we recycle one of the first N linetypes?	*/
2879 	if (tag > mono_recycle_count && mono_recycle_count > 0) {
2880 	    tag = (tag-1) % mono_recycle_count + 1;
2881 	    goto recycle;
2882 	}
2883 
2884 	return;
2885     }
2886 
2887     this = first_perm_linestyle;
2888     while (this != NULL) {
2889 	if (this->tag == tag) {
2890 	    /* Always load color, width, and dash properties */
2891 	    lp->l_type = this->lp_properties.l_type;
2892 	    lp->l_width = this->lp_properties.l_width;
2893 	    lp->pm3d_color = this->lp_properties.pm3d_color;
2894 	    lp->d_type = this->lp_properties.d_type;
2895 	    lp->custom_dash_pattern = this->lp_properties.custom_dash_pattern;
2896 
2897 	    /* Needed in version 5.0 to handle old terminals (pbm hpgl ...) */
2898 	    /* with no support for user-specified colors */
2899 	    if (term && term->set_color == null_set_color)
2900 		lp->l_type = tag;
2901 
2902 	    /* Do not recycle point properties. */
2903 	    /* FIXME: there should be a separate command "set pointtype cycle N" */
2904 	    if (!recycled) {
2905 	    	lp->p_type = this->lp_properties.p_type;
2906 	    	lp->p_interval = this->lp_properties.p_interval;
2907 	    	lp->p_size = this->lp_properties.p_size;
2908 	    	memcpy(lp->p_char, this->lp_properties.p_char, sizeof(lp->p_char));
2909 	    }
2910 	    return;
2911 	} else {
2912 	    this = this->next;
2913 	}
2914     }
2915 
2916     /* This linetype wasn't defined explicitly.		*/
2917     /* Should we recycle one of the first N linetypes?	*/
2918     if (tag > linetype_recycle_count && linetype_recycle_count > 0) {
2919 	tag = (tag-1) % linetype_recycle_count + 1;
2920 	recycled = TRUE;
2921 	goto recycle;
2922     }
2923 
2924     /* No user-defined linetype with this tag; fall back to default line type. */
2925     /* NB: We assume that the remaining fields of lp have been initialized. */
2926     lp->l_type = tag - 1;
2927     lp->pm3d_color.type = TC_LT;
2928     lp->pm3d_color.lt = lp->l_type;
2929     lp->d_type = DASHTYPE_SOLID;
2930     lp->p_type = (tag <= 0) ? -1 : tag - 1;
2931 }
2932 
2933 /*
2934  * Version 5 maintains a parallel set of linetypes for "set monochrome" mode.
2935  * This routine allocates space and initializes the default set.
2936  */
2937 void
init_monochrome()2938 init_monochrome()
2939 {
2940     struct lp_style_type mono_default[] = DEFAULT_MONO_LINETYPES;
2941 
2942     if (first_mono_linestyle == NULL) {
2943 	int i, n = sizeof(mono_default) / sizeof(struct lp_style_type);
2944 	struct linestyle_def *new;
2945 	/* copy default list into active list */
2946 	for (i=n; i>0; i--) {
2947 	    new = gp_alloc(sizeof(struct linestyle_def), NULL);
2948 	    new->next = first_mono_linestyle;
2949 	    new->lp_properties = mono_default[i-1];
2950 	    new->tag = i;
2951 	    first_mono_linestyle = new;
2952 	}
2953     }
2954 }
2955 
2956 /*
2957  * Totally bogus estimate of TeX string lengths.
2958  * Basically
2959  * - don't count anything inside square braces
2960  * - count regexp \[a-zA-z]* as a single character
2961  * - ignore characters {}$^_
2962  */
2963 int
strlen_tex(const char * str)2964 strlen_tex(const char *str)
2965 {
2966     const char *s = str;
2967     int len = 0;
2968 
2969     if (!strpbrk(s, "{}$[]\\")) {
2970 	len = strlen(s);
2971 	FPRINTF((stderr,"strlen_tex(\"%s\") = %d\n",s,len));
2972 	return len;
2973     }
2974 
2975     while (*s) {
2976 	switch (*s) {
2977 	case '[':
2978 		while (*s && *s != ']') s++;
2979 		if (*s) s++;
2980 		break;
2981 	case '\\':
2982 		s++;
2983 		while (*s && isalpha((unsigned char)*s)) s++;
2984 		len++;
2985 		break;
2986 	case '{':
2987 	case '}':
2988 	case '$':
2989 	case '_':
2990 	case '^':
2991 		s++;
2992 		break;
2993 	default:
2994 		s++;
2995 		len++;
2996 	}
2997     }
2998 
2999 
3000     FPRINTF((stderr,"strlen_tex(\"%s\") = %d\n",str,len));
3001     return len;
3002 }
3003 
3004 /* The check for asynchronous events such as hotkeys and mouse clicks is
3005  * normally done in term->waitforinput() while waiting for the next input
3006  * from the command line.  If input is currently coming from a file or
3007  * pipe instead, as with a "load" command, then this path would not be
3008  * triggered automatically and these events would back up until input
3009  * returned to the command line.  These code paths can explicitly call
3010  * check_for_mouse_events() so that event processing is handled sooner.
3011  */
3012 void
check_for_mouse_events()3013 check_for_mouse_events()
3014 {
3015 #ifdef USE_MOUSE
3016     if (term_initialised && term->waitforinput) {
3017 	term->waitforinput(TERM_ONLY_CHECK_MOUSING);
3018     }
3019 #endif
3020 #ifdef _WIN32
3021     /* Process windows GUI events (e.g. for text window, or wxt and windows terminals) */
3022     WinMessageLoop();
3023     /* On Windows, Ctrl-C only sets this flag. */
3024     /* The next block duplicates the behaviour of inter(). */
3025     if (ctrlc_flag) {
3026 	ctrlc_flag = FALSE;
3027 	term_reset();
3028 	putc('\n', stderr);
3029 	fprintf(stderr, "Ctrl-C detected!\n");
3030 	bail_to_command_line();	/* return to prompt */
3031     }
3032 #endif
3033 }
3034