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