Commit message: Better error handling for faulty font syntax

A missing close-quote in an enhanced text font specification could
cause a segfault.
Bug #2303
CVE: CVE-2020-25412
CWE: CWE-787
Vulnerability description: com_line() in command.c in gnuplot 5.4 leads to an out-of-bounds-write from strncpy() that may lead to arbitrary code execution.

Project: gnuplot

lp_use_properties(struct lp_style_type *lp, int tag)
{
    /*  This function looks for a linestyle defined by 'tag' and copies
     *  its data into the structure 'lp'.
     */

    struct linestyle_def *this;
    int save_flags = lp->flags;

    this = first_linestyle;
    while (this != NULL) {
	if (this->tag == tag) {
	    *lp = this->lp_properties;
	    lp->flags = save_flags;
	    return;
	} else {
	    this = this->next;
	}
    }

    /* No user-defined style with this tag; fall back to default line type. */
    load_linetype(lp, tag);
}

Project: gnuplot

on_page(int x, int y)
{
    if (term->flags & TERM_CAN_CLIP)
	return TRUE;

    if ((0 < x && x < term->xmax) && (0 < y && y < term->ymax))
	return TRUE;

    return FALSE;
}

Project: gnuplot

graphics_null()
{
    fprintf(stderr,
	    "WARNING: Plotting with an 'unknown' terminal.\n"
	    "No output will be generated. Please select a terminal with 'set terminal'.\n");
}

Project: gnuplot

ignore_enhanced(TBOOLEAN flag)
{
    ignore_enhanced_text = flag;
}

Project: gnuplot

term_end_multiplot()
{
    FPRINTF((stderr, "term_end_multiplot()\n"));
    if (!multiplot)
	return;

    if (term_suspended) {
	if (term->resume)
	    (*term->resume) ();
	term_suspended = FALSE;
    }

    multiplot_end();

    term_end_plot();
#ifdef USE_MOUSE
    UpdateStatusline();
#endif
}

Project: gnuplot

escape_reserved_chars(const char *str, const char *reserved)
{
    int i;
    char *escaped_str;
    int newsize = strlen(str);

	/* Count number of reserved characters */
	for (i = 0; str[i] != '\0'; i++) {
	    if (strchr(reserved, str[i]))
		newsize++;
	}
	escaped_str = gp_alloc(newsize + 1, NULL);

	/* Prefix each reserved character with a backslash */
	for (i = 0, newsize = 0; str[i] != '\0'; i++) {
	    if (strchr(reserved, str[i]))
		escaped_str[newsize++] = '\\';
	    escaped_str[newsize++] = str[i];
	}
	escaped_str[newsize] = '\0';

    return escaped_str;
}

Project: gnuplot

init_terminal()
{
    char *term_name = DEFAULTTERM;
#if defined(__BEOS__) || defined(X11)
    char *env_term = NULL;      /* from TERM environment var */
#endif
#ifdef X11
    char *display = NULL;
#endif
    char *gnuterm = NULL;

    /* GNUTERM environment variable is primary */
    gnuterm = getenv("GNUTERM");
    if (gnuterm != (char *) NULL) {
	/* April 2017 - allow GNUTERM to include terminal options */
	char *set_term = "set term ";
	char *set_term_command = gp_alloc(strlen(set_term) + strlen(gnuterm) + 4, NULL);
	strcpy(set_term_command, set_term);
	strcat(set_term_command, gnuterm);
	do_string(set_term_command);
	free(set_term_command);
	/* replicate environmental variable GNUTERM for internal use */
	Gstring(&(add_udv_by_name("GNUTERM")->udv_value), gp_strdup(gnuterm));
	return;

    } else {

#ifdef VMS
	term_name = vms_init();
#endif /* VMS */

	if (term_name == (char *) NULL
            && getenv ("DOMTERM") != NULL)
          term_name = "domterm";

#ifdef __BEOS__
	env_term = getenv("TERM");
	if (term_name == (char *) NULL
	    && env_term != (char *) NULL && strcmp(env_term, "beterm") == 0)
	    term_name = "be";
#endif /* BeOS */

#ifdef QTTERM
	if (term_name == (char *) NULL)
	    term_name = "qt";
#endif

#ifdef WXWIDGETS
	if (term_name == (char *) NULL)
	    term_name = "wxt";
#endif

#ifdef _WIN32
	if (term_name == (char *) NULL)
	    term_name = "win";
#endif /* _WIN32 */

#if defined(__APPLE__) && defined(__MACH__) && defined(HAVE_FRAMEWORK_AQUATERM)
	/* Mac OS X with AquaTerm installed */
	term_name = "aqua";
#endif

#ifdef X11
	env_term = getenv("TERM");      /* try $TERM */
	if (term_name == (char *) NULL
	    && env_term != (char *) NULL && strcmp(env_term, "xterm") == 0)
	    term_name = "x11";
	display = getenv("DISPLAY");
	if (term_name == (char *) NULL && display != (char *) NULL)
	    term_name = "x11";
	if (X11_Display)
	    term_name = "x11";
#endif /* x11 */

#ifdef DJGPP
	term_name = "svga";
#endif

#ifdef GRASS
	term_name = "grass";
#endif

#ifdef OS2
/* amai: Note that we do some checks above and now overwrite any
   results. Perhaps we may disable checks above!? */
#ifdef X11
/* WINDOWID is set in sessions like xterm, etc.
   DISPLAY is also mandatory. */
	env_term = getenv("WINDOWID");
	display  = getenv("DISPLAY");
	if ((env_term != (char *) NULL) && (display != (char *) NULL))
	    term_name = "x11";
	else
#endif          /* X11 */
	    term_name = "pm";
#endif /*OS2 */

    }

    /* We have a name, try to set term type */
    if (term_name != NULL && *term_name != '\0') {
	int namelength = strlen(term_name);
	struct udvt_entry *name = add_udv_by_name("GNUTERM");

	Gstring(&name->udv_value, gp_strdup(term_name));

	if (strchr(term_name,' '))
	    namelength = strchr(term_name,' ') - term_name;

	/* Force the terminal to initialize default fonts, etc.	This prevents */
	/* segfaults and other strangeness if you set GNUTERM to "post" or    */
	/* "png" for example. However, calling X11_options() is expensive due */
	/* to the fork+execute of gnuplot_x11 and x11 can tolerate not being  */
	/* initialized until later.                                           */
	/* Note that gp_input_line[] is blank at this point.	              */
	if (change_term(term_name, namelength)) {
	    if (strcmp(term->name,"x11"))
		term->options();
	    return;
	}
	fprintf(stderr, "Unknown or ambiguous terminal name '%s'\n", term_name);
    }
    change_term("unknown", 7);
}

Project: gnuplot

term_start_multiplot()
{
    FPRINTF((stderr, "term_start_multiplot()\n"));
    multiplot_start();
#ifdef USE_MOUSE
    UpdateStatusline();
#endif
}

Project: gnuplot

stylefont(const char *fontname, TBOOLEAN isbold, TBOOLEAN isitalic)
{
    int div;
    char *markup = gp_alloc( strlen(fontname) + 16, "font markup");
    strcpy(markup, fontname);
    /* base font name can be followed by ,<size> or :Variant */
    if ((div = strcspn(markup,",:")))
	markup[div] = '\0';
    if (isbold)
	strcat(markup, ":Bold");
    if (isitalic)
	strcat(markup, ":Italic");

    FPRINTF((stderr, "MARKUP FONT: %s -> %s\n", fontname, markup));
    return markup;
}

Project: gnuplot

style_from_fill(struct fill_style_type *fs)
{
    int fillpar, style;

    switch( fs->fillstyle ) {
    case FS_SOLID:
    case FS_TRANSPARENT_SOLID:
	fillpar = fs->filldensity;
	style = ((fillpar & 0xfff) << 4) + fs->fillstyle;
	break;
    case FS_PATTERN:
    case FS_TRANSPARENT_PATTERN:
	fillpar = fs->fillpattern;
	style = ((fillpar & 0xfff) << 4) + fs->fillstyle;
	break;
    case FS_EMPTY:
    default:
	/* solid fill with background color */
	style = FS_EMPTY;
	break;
    }

    return style;
}

Project: gnuplot

closepath()
{
    if (term->path)
	(*term->path)(1);
}

Project: gnuplot

line_and_point(unsigned int x, unsigned int y, int number)
{
    /* temporary(?) kludge to allow terminals with bad linetypes
       to make nice marks */

    (*term->linetype) (NICE_LINE);
    do_point(x, y, number);
}

Project: gnuplot

term_suspend()
{
    FPRINTF((stderr, "term_suspend()\n"));
    if (term_initialised && !term_suspended && term->suspend) {
	FPRINTF((stderr, "- calling term->suspend()\n"));
	(*term->suspend) ();
	term_suspended = TRUE;
    }
}

Project: gnuplot

list_terms()
{
    int i;
    char *line_buffer = gp_alloc(BUFSIZ, "list_terms");
    int sort_idxs[TERMCOUNT];

    /* sort terminal types alphabetically */
    for( i = 0; i < TERMCOUNT; i++ )
	sort_idxs[i] = i;
    qsort( sort_idxs, TERMCOUNT, sizeof(int), termcomp );
    /* now sort_idxs[] contains the sorted indices */

    StartOutput();
    strcpy(line_buffer, "\nAvailable terminal types:\n");
    OutLine(line_buffer);

    for (i = 0; i < TERMCOUNT; i++) {
	sprintf(line_buffer, "  %15s  %s\n",
		term_tbl[sort_idxs[i]].name,
		term_tbl[sort_idxs[i]].description);
	OutLine(line_buffer);
    }

    EndOutput();
    free(line_buffer);
}

Project: gnuplot

strlen_tex(const char *str)
{
    const char *s = str;
    int len = 0;

    if (!strpbrk(s, "{}$[]\\")) {
	len = strlen(s);
	FPRINTF((stderr,"strlen_tex(\"%s\") = %d\n",s,len));
	return len;
    }

    while (*s) {
	switch (*s) {
	case '[':
		while (*s && *s != ']') s++;
		if (*s) s++;
		break;
	case '\\':
		s++;
		while (*s && isalpha((unsigned char)*s)) s++;
		len++;
		break;
	case '{':
	case '}':
	case '$':
	case '_':
	case '^':
		s++;
		break;
	default:
		s++;
		len++;
	}
    }


    FPRINTF((stderr,"strlen_tex(\"%s\") = %d\n",str,len));
    return len;
}

Project: gnuplot

null_linewidth(double s)
{
    (void) s;                   /* avoid -Wunused warning */
}

Project: gnuplot

term_apply_lp_properties(struct lp_style_type *lp)
{
    /*  This function passes all the line and point properties to the
     *  terminal driver and issues the corresponding commands.
     *
     *  Alas, sometimes it might be necessary to give some help to
     *  this function by explicitly issuing additional '(*term)(...)'
     *  commands.
     */
    int lt = lp->l_type;
    int dt = lp->d_type;
    t_dashtype custom_dash_pattern = lp->custom_dash_pattern;
    t_colorspec colorspec = lp->pm3d_color;

    if ((lp->flags & LP_SHOW_POINTS)) {
	/* change points, too
	 * Currently, there is no 'pointtype' function.  For points
	 * there is a special function also dealing with (x,y) co-
	 * ordinates.
	 */
	if (lp->p_size < 0)
	    (*term->pointsize) (pointsize);
	else
	    (*term->pointsize) (lp->p_size);
    }
    /*  _first_ set the line width, _then_ set the line type !
     *  The linetype might depend on the linewidth in some terminals.
     */
    (*term->linewidth) (lp->l_width);

    /* LT_DEFAULT (used only by "set errorbars"?) means don't change it */
    if (lt == LT_DEFAULT)
	;
    else

    /* The paradigm for handling linetype and dashtype in version 5 is */
    /* linetype < 0 (e.g. LT_BACKGROUND, LT_NODRAW) means some special */
    /* category that will be handled directly by term->linetype().     */
    /* linetype > 0 is now redundant. It used to encode both a color   */
    /* and a dash pattern.  Now we have separate mechanisms for those. */
    if (LT_COLORFROMCOLUMN < lt && lt < 0)
	(*term->linetype) (lt);
    else if (term->set_color == null_set_color) {
	(*term->linetype) (lt-1);
	return;
    } else /* All normal lines will be solid unless a dashtype is given */
	(*term->linetype) (LT_SOLID);

    /* Version 5.3
     * If the line is not wanted at all, setting dashtype and color can only hurt
     */
    if (lt == LT_NODRAW)
	return;

    /* Apply dashtype or user-specified dash pattern, which may override  */
    /* the terminal-specific dot/dash pattern belonging to this linetype. */
    if (lt == LT_AXIS)
	; /* LT_AXIS is a special linetype that may incorporate a dash pattern */
    else if (dt == DASHTYPE_CUSTOM)
	(*term->dashtype) (dt, &custom_dash_pattern);
    else if (dt == DASHTYPE_SOLID)
	(*term->dashtype) (dt, NULL);
    else if (dt >= 0)
	/* The null_dashtype() routine or a version 5 terminal's private  */
	/* dashtype routine converts this into a call to term->linetype() */
	/* yielding the same result as in version 4 except possibly for a */
	/* different line width.					  */
	(*term->dashtype) (dt, NULL);

    /* Finally adjust the color of the line */
    apply_pm3dcolor(&colorspec);
}

Project: gnuplot

null_justify_text(enum JUSTIFY just)
{
    return (just == LEFT);
}

Project: gnuplot

termcomp(const generic *arga, const generic *argb)
{
    const int *a = arga;
    const int *b = argb;

    return( strcasecmp( term_tbl[*a].name, term_tbl[*b].name ) );
}

Project: gnuplot

test_term()
{
    struct termentry *t = term;
    const char *str;
    int x, y, xl, yl, i;
    int xmax_t, ymax_t, x0, y0;
    char label[MAX_ID_LEN];
    int key_entry_height;
    int p_width;
    TBOOLEAN already_in_enhanced_text_mode;
    static t_colorspec black = BLACK_COLORSPEC;

    already_in_enhanced_text_mode = t->flags & TERM_ENHANCED_TEXT;
    if (!already_in_enhanced_text_mode)
	do_string("set termopt enh");

    term_start_plot();
    screen_ok = FALSE;
    xmax_t = (t->xmax * xsize);
    ymax_t = (t->ymax * ysize);
    x0 = (xoffset * t->xmax);
    y0 = (yoffset * t->ymax);

    p_width = pointsize * t->h_tic;
    key_entry_height = pointsize * t->v_tic * 1.25;
    if (key_entry_height < t->v_char)
	key_entry_height = t->v_char;

    /* Sync point for epslatex text positioning */
    (*t->layer)(TERM_LAYER_FRONTTEXT);

    /* border linetype */
    (*t->linewidth) (1.0);
    (*t->linetype) (LT_BLACK);
    newpath();
    (*t->move) (x0, y0);
    (*t->vector) (x0 + xmax_t - 1, y0);
    (*t->vector) (x0 + xmax_t - 1, y0 + ymax_t - 1);
    (*t->vector) (x0, y0 + ymax_t - 1);
    (*t->vector) (x0, y0);
    closepath();

    /* Echo back the current terminal type */
    if (!strcmp(term->name,"unknown"))
	int_error(NO_CARET, "terminal type is unknown");
    else {
	char tbuf[64];
	(void) (*t->justify_text) (LEFT);
	sprintf(tbuf,"%s  terminal test", term->name);
	(*t->put_text) (x0 + t->h_char * 2, y0 + ymax_t - t->v_char, tbuf);
	sprintf(tbuf, "gnuplot version %s.%s  ", gnuplot_version, gnuplot_patchlevel);
	(*t->put_text) (x0 + t->h_char * 2, y0 + ymax_t - t->v_char * 2.25, tbuf);
    }

    (*t->linetype) (LT_AXIS);
    (*t->move) (x0 + xmax_t / 2, y0);
    (*t->vector) (x0 + xmax_t / 2, y0 + ymax_t - 1);
    (*t->move) (x0, y0 + ymax_t / 2);
    (*t->vector) (x0 + xmax_t - 1, y0 + ymax_t / 2);

    /* How well can we estimate width and height of characters?
     * Textbox fill shows true size, surrounding box shows the generic estimate
     * used to reserve space during plot layout.
     */
    if (TRUE) {
	struct text_label sample = EMPTY_LABELSTRUCT;
	struct textbox_style save_opts = textbox_opts[0];
	textbox_style *textbox = &textbox_opts[0];
	sample.text = "12345678901234567890";
	sample.pos = CENTRE;
	sample.boxed = -1;
	textbox->opaque = TRUE;
	textbox->noborder = TRUE;
	textbox->fillcolor.type = TC_RGB;
	textbox->fillcolor.lt = 0xccccee;
	/* disable extra space around text */
	textbox->xmargin = 0;
	textbox->ymargin = 0;

	(*t->linetype) (LT_SOLID);
	write_label(xmax_t/2, ymax_t/2, &sample);
	textbox_opts[0] = save_opts;

	sample.boxed = 0;
	sample.text = "true vs. estimated text dimensions";
	write_label(xmax_t/2, ymax_t/2 + 1.5 * t->v_char, &sample);

	newpath();
	(*t->move) (x0 + xmax_t / 2 - t->h_char * 10, y0 + ymax_t / 2 + t->v_char / 2);
	(*t->vector) (x0 + xmax_t / 2 + t->h_char * 10, y0 + ymax_t / 2 + t->v_char / 2);
	(*t->vector) (x0 + xmax_t / 2 + t->h_char * 10, y0 + ymax_t / 2 - t->v_char / 2);
	(*t->vector) (x0 + xmax_t / 2 - t->h_char * 10, y0 + ymax_t / 2 - t->v_char / 2);
	(*t->vector) (x0 + xmax_t / 2 - t->h_char * 10, y0 + ymax_t / 2 + t->v_char / 2);
	closepath();
    }

    /* Test for enhanced text */
    (*t->linetype) (LT_BLACK);
    if (t->flags & TERM_ENHANCED_TEXT) {
	char *tmptext1 =   "Enhanced text:   {x@_{0}^{n+1}}";
	char *tmptext2 = "&{Enhanced text:  }{/:Bold Bold}{/:Italic  Italic}";
	(*t->put_text) (x0 + xmax_t * 0.5, y0 + ymax_t * 0.40, tmptext1);
	(*t->put_text) (x0 + xmax_t * 0.5, y0 + ymax_t * 0.35, tmptext2);
	(*t->set_font)("");
	if (!already_in_enhanced_text_mode)
	    do_string("set termopt noenh");
    }

    /* test justification */
    (void) (*t->justify_text) (LEFT);
    (*t->put_text) (x0 + xmax_t / 2, y0 + ymax_t / 2 + t->v_char * 6, "left justified");
    str = "centre+d text";
    if ((*t->justify_text) (CENTRE))
	(*t->put_text) (x0 + xmax_t / 2,
			y0 + ymax_t / 2 + t->v_char * 5, str);
    else
	(*t->put_text) (x0 + xmax_t / 2 - strlen(str) * t->h_char / 2,
			y0 + ymax_t / 2 + t->v_char * 5, str);
    str = "right justified";
    if ((*t->justify_text) (RIGHT))
	(*t->put_text) (x0 + xmax_t / 2,
			y0 + ymax_t / 2 + t->v_char * 4, str);
    else
	(*t->put_text) (x0 + xmax_t / 2 - strlen(str) * t->h_char,
			y0 + ymax_t / 2 + t->v_char * 4, str);

    /* test tic size */
    (*t->linetype)(2);
    (*t->move) ((unsigned int) (x0 + xmax_t / 2 + t->h_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)), y0 + (unsigned int) ymax_t - 1);
    (*t->vector) ((unsigned int) (x0 + xmax_t / 2 + t->h_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)),
		  (unsigned int) (y0 + ymax_t - axis_array[FIRST_X_AXIS].ticscale * t->v_tic));
    (*t->move) ((unsigned int) (x0 + xmax_t / 2), y0 + (unsigned int) (ymax_t - t->v_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)));
    (*t->vector) ((unsigned int) (x0 + xmax_t / 2 + axis_array[FIRST_X_AXIS].ticscale * t->h_tic),
		  (unsigned int) (y0 + ymax_t - t->v_tic * (1 + axis_array[FIRST_X_AXIS].ticscale)));
    (void) (*t->justify_text) (RIGHT);
    (*t->put_text) (x0 + (unsigned int) (xmax_t / 2 - 1* t->h_char),
		    y0 + (unsigned int) (ymax_t - t->v_char),
		    "show ticscale");
    (void) (*t->justify_text) (LEFT);
    (*t->linetype)(LT_BLACK);

    /* test line and point types */
    x = x0 + xmax_t - t->h_char * 7 - p_width;
    y = y0 + ymax_t - key_entry_height;
    (*t->pointsize) (pointsize);
    for (i = -2; y > y0 + key_entry_height; i++) {
	struct lp_style_type ls = DEFAULT_LP_STYLE_TYPE;
	ls.l_width = 1;
	load_linetype(&ls,i+1);
	term_apply_lp_properties(&ls);

	(void) sprintf(label, "%d", i + 1);
	if ((*t->justify_text) (RIGHT))
	    (*t->put_text) (x, y, label);
	else
	    (*t->put_text) (x - strlen(label) * t->h_char, y, label);
	(*t->move) (x + t->h_char, y);
	(*t->vector) (x + t->h_char * 5, y);
	if (i >= -1)
	    (*t->point) (x + t->h_char * 6 + p_width / 2, y, i);
	y -= key_entry_height;
    }

    /* test arrows (should line up with rotated text) */
    (*t->linewidth) (1.0);
    (*t->linetype) (0);
    (*t->dashtype) (DASHTYPE_SOLID, NULL);
    x = x0 + 2. * t->v_char;
    y = y0 + ymax_t/2;
    xl = t->h_tic * 7;
    yl = t->v_tic * 7;
    i = curr_arrow_headfilled;
    curr_arrow_headfilled = AS_NOBORDER;
    (*t->arrow) (x, y-yl, x, y+yl, BOTH_HEADS);
    curr_arrow_headfilled = AS_EMPTY;
    (*t->arrow) (x, y, x + xl, y + yl, END_HEAD);
    curr_arrow_headfilled = AS_NOFILL;
    (*t->arrow) (x, y, x + xl, y - yl, END_HEAD);
    curr_arrow_headfilled = i;

    /* test text angle (should match arrows) */
    (*t->linetype)(0);
    str = "rotated ce+ntred text";
    if ((*t->text_angle) (TEXT_VERTICAL)) {
	if ((*t->justify_text) (CENTRE))
	    (*t->put_text) (x0 + t->v_char,
			    y0 + ymax_t / 2, str);
	else
	    (*t->put_text) (x0 + t->v_char,
			    y0 + ymax_t / 2 - strlen(str) * t->h_char / 2, str);
	(*t->justify_text) (LEFT);
	str = "  rotate by +45";
	(*t->text_angle)(45);
	(*t->put_text)(x0 + t->v_char * 3, y0 + ymax_t / 2, str);
	(*t->justify_text) (LEFT);
	str = "  rotate by -45";
	(*t->text_angle)(-45);
	(*t->put_text)(x0 + t->v_char * 3, y0 + ymax_t / 2, str);
    } else {
	(void) (*t->justify_text) (LEFT);
	(*t->put_text) (x0 + t->h_char * 2, y0 + ymax_t / 2, "cannot rotate text");
    }
    (void) (*t->justify_text) (LEFT);
    (void) (*t->text_angle) (0);

    /* test line widths */
    (void) (*t->justify_text) (LEFT);
    xl = xmax_t / 10;
    yl = ymax_t / 25;
    x = x0 + xmax_t * .075;
    y = y0 + yl;

    for (i=1; i<7; i++) {
	(*t->linewidth) ((double)(i)); (*t->linetype)(LT_BLACK);
	(*t->move) (x, y); (*t->vector) (x+xl, y);
	sprintf(label,"  lw %1d", i);
	(*t->put_text) (x+xl, y, label);
	y += yl;
    }
    (*t->put_text) (x, y, "linewidth");

    /* test native dashtypes (_not_ the 'set mono' sequence) */
    (void) (*t->justify_text) (LEFT);
    xl = xmax_t / 10;
    yl = ymax_t / 25;
    x = x0 + xmax_t * .3;
    y = y0 + yl;

    for (i=0; i<5; i++) {
 	(*t->linewidth) (1.0);
	(*t->linetype) (LT_SOLID);
	(*t->dashtype) (i, NULL);
	(*t->set_color)(&black);
	(*t->move) (x, y); (*t->vector) (x+xl, y);
	sprintf(label,"  dt %1d", i+1);
	(*t->put_text) (x+xl, y, label);
	y += yl;
    }
    (*t->put_text) (x, y, "dashtype");

    /* test fill patterns */
    x = x0 + xmax_t * 0.5;
    y = y0;
    xl = xmax_t / 40;
    yl = ymax_t / 8;
    (*t->linewidth) (1.0);
    (*t->linetype)(LT_BLACK);
    (*t->justify_text) (CENTRE);
    (*t->put_text)(x+xl*7, y + yl+t->v_char*1.5, "pattern fill");
    for (i=0; i<9; i++) {
	int style = ((i<<4) + FS_PATTERN);
	if (t->fillbox)
	    (*t->fillbox) ( style, x, y, xl, yl );
	newpath();
	(*t->move)  (x,y);
	(*t->vector)(x,y+yl);
	(*t->vector)(x+xl,y+yl);
	(*t->vector)(x+xl,y);
	(*t->vector)(x,y);
	closepath();
	sprintf(label, "%2d", i);
	(*t->put_text)(x+xl/2, y+yl+t->v_char*0.5, label);
	x += xl * 1.5;
    }

    {
	int cen_x = x0 + (int)(0.70 * xmax_t);
	int cen_y = y0 + (int)(0.83 * ymax_t);
	int radius = xmax_t / 20;

	/* test pm3d -- filled_polygon(), but not set_color() */
	if (t->filled_polygon) {
	    int i, j;
#define NUMBER_OF_VERTICES 6
	    int n = NUMBER_OF_VERTICES;
	    gpiPoint corners[NUMBER_OF_VERTICES+1];
#undef  NUMBER_OF_VERTICES

	    for (j=0; j<=1; j++) {
		int ix = cen_x + j*radius;
		int iy = cen_y - j*radius/2;
		for (i = 0; i < n; i++) {
		    corners[i].x = ix + radius * cos(2*M_PI*i/n);
		    corners[i].y = iy + radius * sin(2*M_PI*i/n);
		}
		corners[n].x = corners[0].x;
		corners[n].y = corners[0].y;
		if (j == 0) {
		    (*t->linetype)(2);
		    corners->style = FS_OPAQUE;
		} else {
		    (*t->linetype)(1);
		    corners->style = FS_TRANSPARENT_SOLID + (50<<4);
		}
		term->filled_polygon(n+1, corners);
	    }
	    str = "filled polygons:";
	} else
	    str = "No filled polygons";
	(*t->linetype)(LT_BLACK);
	i = ((*t->justify_text) (CENTRE)) ? 0 : t->h_char * strlen(str) / 2;
	(*t->put_text) (cen_x - i, cen_y + radius + t->v_char * 0.5, str);
    }

    term_end_plot();
}

Project: gnuplot

write_multiline(
    int x, int y,
    char *text,
    JUSTIFY hor,                /* horizontal ... */
    VERT_JUSTIFY vert,          /* ... and vertical just - text in hor direction despite angle */
    int angle,                  /* assume term has already been set for this */
    const char *font)           /* NULL or "" means use default */
{
    struct termentry *t = term;
    char *p = text;

    if (!p)
	return;

    /* EAM 9-Feb-2003 - Set font before calculating sizes */
    if (font && *font)
	(*t->set_font) (font);

    if (vert != JUST_TOP) {
	/* count lines and adjust y */
	int lines = 0;          /* number of linefeeds - one fewer than lines */
	while (*p) {
	    if (*p++ == '\n')
		++lines;
	}
	if (angle)
	    x -= (vert * lines * t->v_char) / 2;
	else
	    y += (vert * lines * t->v_char) / 2;
    }

    for (;;) {                  /* we will explicitly break out */

	if ((text != NULL) && (p = strchr(text, '\n')) != NULL)
	    *p = 0;             /* terminate the string */

	if ((*t->justify_text) (hor)) {
	    if (on_page(x, y))
		(*t->put_text) (x, y, text);
	} else {
	    int len = estimate_strlen(text, NULL);
	    int hfix, vfix;

	    if (angle == 0) {
		hfix = hor * t->h_char * len / 2;
		vfix = 0;
	    } else {
		/* Attention: This relies on the numeric values of enum JUSTIFY! */
		hfix = hor * t->h_char * len * cos(angle * DEG2RAD) / 2 + 0.5;
		vfix = hor * t->v_char * len * sin(angle * DEG2RAD) / 2 + 0.5;
	    }
		if (on_page(x - hfix, y - vfix))
		    (*t->put_text) (x - hfix, y - vfix, text);
	}
	if (angle == 90 || angle == TEXT_VERTICAL)
	    x += t->v_char;
	else if (angle == -90 || angle == -TEXT_VERTICAL)
	    x -= t->v_char;
	else
	    y -= t->v_char;

	if (!p)
	    break;
	else {
	    /* put it back */
	    *p = '\n';
	}

	text = p + 1;
    }                           /* unconditional branch back to the for(;;) - just a goto ! */

    if (font && *font)
	(*t->set_font) ("");

}

Project: gnuplot

null_dashtype(int type, t_dashtype *custom_dash_pattern)
{
    (void) custom_dash_pattern;	/* ignore */
    /*
     * If the terminal does not support user-defined dashtypes all we can do
     * do is fall through to the old (pre-v5) assumption that the dashtype,
     * if any, is part of the linetype.  We also assume that the color will
     * be adjusted after this.
     */
    if (type <= 0)
	type = LT_SOLID;
    term->linetype(type);
}

Project: gnuplot

estimate_strlen(const char *text, double *height)
{
    int len;
    char *s;
    double estimated_fontheight = 1.0;

    if ((term->flags & TERM_IS_LATEX))
	return strlen_tex(text);

#ifdef GP_ENH_EST
    if (strchr(text,'\n') || (term->flags & TERM_ENHANCED_TEXT)) {
	struct termentry *tsave = term;
	term = &ENHest;
	term->put_text(0,0,text);
	len = term->xmax;
	estimated_fontheight = term->ymax / 10.;
	term = tsave;
	/* Assume that unicode escape sequences  \U+xxxx will generate a single character */
	/* ENHest_plaintext is filled in by the put_text() call to estimate.trm           */
	s = ENHest_plaintext;
	while ((s = contains_unicode(s)) != NULL) {
	    len -= 6;
	    s += 6;
	}
	FPRINTF((stderr,"Estimating length %d height %g for enhanced text \"%s\"",
		len, estimated_fontheight, text));
	FPRINTF((stderr,"  plain text \"%s\"\n", ENHest_plaintext));
    } else if (encoding == S_ENC_UTF8)
	len = strwidth_utf8(text);
    else
#endif
	len = strlen(text);

    if (height)
	*height = estimated_fontheight;

    return len;
}

Project: gnuplot

parse_term_size( float *xsize, float *ysize, size_units default_units )
{
    size_units units = default_units;

    if (END_OF_COMMAND)
	int_error(c_token, "size requires two numbers:  xsize, ysize");
    *xsize = real_expression();
    if (almost_equals(c_token,"in$ches")) {
	c_token++;
	units = INCHES;
    } else if (equals(c_token,"cm")) {
	c_token++;
	units = CM;
    }
    switch (units) {
    case INCHES:	*xsize *= gp_resolution; break;
    case CM:		*xsize *= (float)gp_resolution / 2.54; break;
    case PIXELS:
    default:		 break;
    }

    if (!equals(c_token++,","))
	int_error(c_token, "size requires two numbers:  xsize, ysize");
    *ysize = real_expression();
    if (almost_equals(c_token,"in$ches")) {
	c_token++;
	units = INCHES;
    } else if (equals(c_token,"cm")) {
	c_token++;
	units = CM;
    }
    switch (units) {
    case INCHES:	*ysize *= gp_resolution; break;
    case CM:		*ysize *= (float)gp_resolution / 2.54; break;
    case PIXELS:
    default:		 break;
    }

    if (*xsize < 1 || *ysize < 1)
	int_error(c_token, "size: out of range");

    return units;
}

Project: gnuplot

term_set_output(char *dest)
{
    FILE *f = NULL;

    FPRINTF((stderr, "term_set_output\n"));
    assert(dest == NULL || dest != outstr);

    if (multiplot) {
	fputs("In multiplot mode you can't change the output\n", stderr);
	return;
    }
    if (term && term_initialised) {
	(*term->reset) ();
	term_initialised = FALSE;
	/* switch off output to special postscript file (if used) */
	gppsfile = NULL;
    }
    if (dest == NULL) {         /* stdout */
	term_close_output();
    } else {

#if defined(PIPES)
	if (*dest == '|') {
	    restrict_popen();
#if defined(_WIN32 ) || defined(MSDOS)
	    if (term && (term->flags & TERM_BINARY))
		f = popen(dest + 1, "wb");
	    else
		f = popen(dest + 1, "w");
#else
	    f = popen(dest + 1, "w");
#endif
	    if (f == (FILE *) NULL)
		os_error(c_token, "cannot create pipe; output not changed");
	    else
		output_pipe_open = TRUE;
	} else {
#endif /* PIPES */

#ifdef _WIN32
	if (outstr && stricmp(outstr, "PRN") == 0) {
	    /* we can't call open_printer() while printer is open, so */
	    close_printer(gpoutfile);   /* close printer immediately if open */
	    gpoutfile = stdout; /* and reset output to stdout */
	    free(outstr);
	    outstr = NULL;
	}
	if (stricmp(dest, "PRN") == 0) {
	    if ((f = open_printer()) == (FILE *) NULL)
		os_error(c_token, "cannot open printer temporary file; output may have changed");
	} else
#endif

	{
#if defined (MSDOS)
	    if (outstr && (0 == stricmp(outstr, dest))) {
		/* On MSDOS, you cannot open the same file twice and
		 * then close the first-opened one and keep the second
		 * open, it seems. If you do, you get lost clusters
		 * (connection to the first version of the file is
		 * lost, it seems). */
		/* FIXME: this is not yet safe enough. You can fool it by
		 * specifying the same output file in two different ways
		 * (relative vs. absolute path to file, e.g.) */
		term_close_output();
	    }
#endif
	    if (term && (term->flags & TERM_BINARY))
		f = FOPEN_BINARY(dest);
	    else
		f = fopen(dest, "w");

	    if (f == (FILE *) NULL)
		os_error(c_token, "cannot open file; output not changed");
	}
#if defined(PIPES)
	}
#endif

	term_close_output();
	gpoutfile = f;
	outstr = dest;
	opened_binary = (term && (term->flags & TERM_BINARY));
    }
}

Project: gnuplot

enhanced_recursion(
    const char *p,
    TBOOLEAN brace,
    char *fontname,
    double fontsize,
    double base,
    TBOOLEAN widthflag,
    TBOOLEAN showflag,
    int overprint)
{
    TBOOLEAN wasitalic, wasbold;

    /* Keep track of the style of the font passed in at this recursion level */
    wasitalic = (strstr(fontname, ":Italic") != NULL);
    wasbold = (strstr(fontname, ":Bold") != NULL);

    FPRINTF((stderr, "RECURSE WITH \"%s\", %d %s %.1f %.1f %d %d %d",
		p, brace, fontname, fontsize, base, widthflag, showflag, overprint));

    /* Start each recursion with a clean string */
    (term->enhanced_flush)();

    if (base + fontsize > enhanced_max_height) {
	enhanced_max_height = base + fontsize;
	ENH_DEBUG(("Setting max height to %.1f\n", enhanced_max_height));
    }

    if (base < enhanced_min_height) {
	enhanced_min_height = base;
	ENH_DEBUG(("Setting min height to %.1f\n", enhanced_min_height));
    }

    while (*p) {
	double shift;

	/*
	 * EAM Jun 2009 - treating bytes one at a time does not work for multibyte
	 * encodings, including utf-8. If we hit a byte with the high bit set, test
	 * whether it starts a legal UTF-8 sequence and if so copy the whole thing.
	 * Other multibyte encodings are still a problem.
	 * Gnuplot's other defined encodings are all single-byte; for those we
	 * really do want to treat one byte at a time.
	 */
	if ((*p & 0x80) && (encoding == S_ENC_DEFAULT || encoding == S_ENC_UTF8)) {
	    unsigned long utf8char;
	    const char *nextchar = p;

	    (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
	    if (utf8toulong(&utf8char, &nextchar)) {	/* Legal UTF8 sequence */
		while (p < nextchar)
		    (term->enhanced_writec)(*p++);
		p--;
	    } else {					/* Some other multibyte encoding? */
		(term->enhanced_writec)(*p);
	    }
/* shige : for Shift_JIS */
	} else if ((*p & 0x80) && (encoding == S_ENC_SJIS)) {
	    (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
	    (term->enhanced_writec)(*(p++));
	    (term->enhanced_writec)(*p);
	} else

	switch (*p) {
	case '}'  :
	    /*{{{  deal with it*/
	    if (brace)
		return (p);

	    int_warn(NO_CARET, "enhanced text parser - spurious }");
	    break;
	    /*}}}*/

	case '_'  :
	case '^'  :
	    /*{{{  deal with super/sub script*/
	    shift = (*p == '^') ? 0.5 : -0.3;
	    (term->enhanced_flush)();
	    p = enhanced_recursion(p + 1, FALSE, fontname, fontsize * 0.8,
			      base + shift * fontsize, widthflag,
			      showflag, overprint);
	    break;
	    /*}}}*/
	case '{'  :
	    {
		TBOOLEAN isitalic = FALSE, isbold = FALSE, isnormal = FALSE;
		const char *start_of_fontname = NULL;
		const char *end_of_fontname = NULL;
		char *localfontname = NULL;
		char ch;
		double f = fontsize, ovp;

		/* Mar 2014 - this will hold "fontfamily{:Italic}{:Bold}" */
		char *styledfontname = NULL;

		/*{{{  recurse (possibly with a new font) */

		ENH_DEBUG(("Dealing with {\n"));

		/* 30 Sep 2016:  Remove incorrect whitespace-eating loop going */
		/* waaay back to 31-May-2000 */        /* while (*++p == ' '); */
		++p;
		/* get vertical offset (if present) for overprinted text */
		if (overprint == 2) {
		    char *end;
		    ovp = strtod(p,&end);
		    p = end;
		    if (term->flags & TERM_IS_POSTSCRIPT)
			base = ovp*f;
		    else
			base += ovp*f;
		}
		--p;

		if (*++p == '/') {
		    /* then parse a fontname, optional fontsize */
		    while (*++p == ' ')
			;       /* do nothing */
		    if (*p=='-') {
			while (*++p == ' ')
			    ;   /* do nothing */
		    }
		    start_of_fontname = p;

		    /* Allow font name to be in quotes.
		     * This makes it possible to handle font names containing spaces.
		     */
		    if (*p == '\'' || *p == '"') {
			++p;
			while (*p != '\0' && *p != '}' && *p != *start_of_fontname)
			    ++p;
			if (*p != *start_of_fontname) {
			    int_warn(NO_CARET, "cannot interpret font name %s", start_of_fontname);
			    p = start_of_fontname + 1;
			}
			start_of_fontname++;
			end_of_fontname = p++;
			ch = *p;
		    } else {

		    /* Normal unquoted font name */
			while ((ch = *p) > ' ' && ch != '=' && ch != '*' && ch != '}' && ch != ':')
			    ++p;
			end_of_fontname = p;
		    }

		    do {
			if (ch == '=') {
			    /* get optional font size */
			    char *end;
			    p++;
			    ENH_DEBUG(("Calling strtod(\"%s\") ...", p));
			    f = strtod(p, &end);
			    p = end;
			    ENH_DEBUG(("Returned %.1f and \"%s\"\n", f, p));

			    if (f == 0)
				f = fontsize;
			    else
				f *= enhanced_fontscale;  /* remember the scaling */

			    ENH_DEBUG(("Font size %.1f\n", f));
			} else if (ch == '*') {
			    /* get optional font size scale factor */
			    char *end;
			    p++;
			    ENH_DEBUG(("Calling strtod(\"%s\") ...", p));
			    f = strtod(p, &end);
			    p = end;
			    ENH_DEBUG(("Returned %.1f and \"%s\"\n", f, p));

			    if (f)
				f *= fontsize;  /* apply the scale factor */
			    else
				f = fontsize;

			    ENH_DEBUG(("Font size %.1f\n", f));
			} else if (ch == ':') {
			    /* get optional style markup attributes */
			    p++;
			    if (!strncmp(p,"Bold",4))
				isbold = TRUE;
			    if (!strncmp(p,"Italic",6))
				isitalic = TRUE;
			    if (!strncmp(p,"Normal",6))
				isnormal = TRUE;
			    while (isalpha((unsigned char)*p)) {p++;}
			}
		    } while (((ch = *p) == '=') || (ch == ':') || (ch == '*'));

		    if (ch == '}')
			int_warn(NO_CARET,"bad syntax in enhanced text string");

		    if (*p == ' ')	/* Eat up a single space following a font spec */
			++p;
		    if (!start_of_fontname || (start_of_fontname == end_of_fontname)) {
			/* Use the font name passed in to us */
			localfontname = gp_strdup(fontname);
		    } else {
			/* We found a new font name {/Font ...} */
			int len = end_of_fontname - start_of_fontname;
			localfontname = gp_alloc(len+1,"localfontname");
			strncpy(localfontname, start_of_fontname, len);
			localfontname[len] = '\0';
		    }
		}
		/*}}}*/

		/* Collect cumulative style markup before passing it in the font name */
		isitalic = (wasitalic || isitalic) && !isnormal;
		isbold = (wasbold || isbold) && !isnormal;

		styledfontname = stylefont(localfontname ? localfontname : fontname,
					    isbold, isitalic);

		p = enhanced_recursion(p, TRUE, styledfontname, f, base,
				  widthflag, showflag, overprint);

		(term->enhanced_flush)();

		free(styledfontname);
		free(localfontname);

		break;
	    } /* case '{' */
	case '@' :
	    /*{{{  phantom box - prints next 'char', then restores currentpoint */
	    (term->enhanced_flush)();
	    (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, 3);
	    p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
			      widthflag, showflag, overprint);
	    (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, 4);
	    break;
	    /*}}}*/

	case '&' :
	    /*{{{  character skip - skips space equal to length of character(s) */
	    (term->enhanced_flush)();

	    p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
			      widthflag, FALSE, overprint);
	    break;
	    /*}}}*/

	case '~' :
	    /*{{{ overprinted text */
	    /* the second string is overwritten on the first, centered
	     * horizontally on the first and (optionally) vertically
	     * shifted by an amount specified (as a fraction of the
	     * current fontsize) at the beginning of the second string

	     * Note that in this implementation neither the under- nor
	     * overprinted string can contain syntax that would result
	     * in additional recursions -- no subscripts,
	     * superscripts, or anything else, with the exception of a
	     * font definition at the beginning of the text */

	    (term->enhanced_flush)();
	    p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
			      widthflag, showflag, 1);
	    (term->enhanced_flush)();
	    if (!*p)
	        break;
	    p = enhanced_recursion(++p, FALSE, fontname, fontsize, base,
			      FALSE, showflag, 2);

	    overprint = 0;   /* may not be necessary, but just in case . . . */
	    break;
	    /*}}}*/

	case '('  :
	case ')'  :
	    /*{{{  an escape and print it */
	    /* special cases */
	    (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
	    if (term->flags & TERM_IS_POSTSCRIPT)
		(term->enhanced_writec)('\\');
	    (term->enhanced_writec)(*p);
	    break;
	    /*}}}*/

	case '\\'  :
	    /*{{{  various types of escape sequences, some context-dependent */
	    (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);

	    /*     Unicode represented as \U+hhhhh where hhhhh is hexadecimal code point.
	     *     For UTF-8 encoding we translate hhhhh to a UTF-8 byte sequence and
	     *     output the bytes one by one.
	     */
	    if (p[1] == 'U' && p[2] == '+') {
		if (encoding == S_ENC_UTF8) {
		    uint32_t codepoint;
		    unsigned char utf8char[8];
		    int i, length;

		    sscanf(&(p[3]), "%5x", &codepoint);
		    length = ucs4toutf8(codepoint, utf8char);
		    p += (codepoint > 0xFFFF) ? 7 : 6;
		    for (i=0; i<length; i++)
			(term->enhanced_writec)(utf8char[i]);
		    break;
		}

	    /*     FIXME: non-utf8 environments not yet supported.
	     *     Note that some terminals may have an alternative way to handle unicode
	     *     escape sequences that is not dependent on encoding.
	     *     E.g. svg and html output could convert to xml sequences &#xhhhh;
	     *     For these cases we must retain the leading backslash so that the
	     *     unicode escape sequence can be recognized by the terminal driver.
	     */
		(term->enhanced_writec)(p[0]);
		break;
	    }

	    /* Enhanced mode always uses \xyz as an octal character representation
	     * but each terminal type must give us the actual output format wanted.
	     * pdf.trm wants the raw character code, which is why we use strtol();
	     * most other terminal types want some variant of "\\%o".
	     */
	    if (p[1] >= '0' && p[1] <= '7') {
		char *e, escape[16], octal[4] = {'\0','\0','\0','\0'};

		octal[0] = *(++p);
		if (p[1] >= '0' && p[1] <= '7') {
		    octal[1] = *(++p);
		    if (p[1] >= '0' && p[1] <= '7')
			octal[2] = *(++p);
		}
		sprintf(escape, enhanced_escape_format, strtol(octal,NULL,8));
		for (e=escape; *e; e++) {
		    (term->enhanced_writec)(*e);
		}
		break;
	    }

	    /* This was the original (prior to version 4) enhanced text code specific
	     * to the reserved characters of PostScript.
	     */
	    if (term->flags & TERM_IS_POSTSCRIPT) {
		if (p[1]=='\\' || p[1]=='(' || p[1]==')') {
		    (term->enhanced_writec)('\\');
		} else if (strchr("^_@&~{}",p[1]) == NULL) {
		    (term->enhanced_writec)('\\');
		    (term->enhanced_writec)('\\');
		    break;
		}
	    }

	    /* Step past the backslash character in the input stream */
	    ++p;

	    /* HBB: Avoid broken output if there's a \ exactly at the end of the line */
	    if (*p == '\0') {
		int_warn(NO_CARET, "enhanced text parser -- spurious backslash");
		break;
	    }

	    /* SVG requires an escaped '&' to be passed as something else */
	    /* FIXME: terminal-dependent code does not belong here */
	    if (*p == '&' && encoding == S_ENC_DEFAULT && !strcmp(term->name, "svg")) {
		(term->enhanced_writec)('\376');
		break;
	    }

	    /* print the character following the backslash */
	    (term->enhanced_writec)(*p);
	    break;
	    /*}}}*/

	default:
	    /*{{{  print it */
	    (term->enhanced_open)(fontname, fontsize, base, widthflag, showflag, overprint);
	    (term->enhanced_writec)(*p);
	    /*}}}*/
	} /* switch (*p) */

	/* like TeX, we only do one character in a recursion, unless it's
	 * in braces
	 */

	if (!brace) {
	    (term->enhanced_flush)();
	    return(p);  /* the ++p in the outer copy will increment us */
	}

	if (*p) /* only not true if { not terminated, I think */
	    ++p;
    } /* while (*p) */

    (term->enhanced_flush)();
    return p;
}

Project: gnuplot

newpath()
{
    if (term->path)
	(*term->path)(0);
}

Project: gnuplot

null_set_color(struct t_colorspec *colorspec)
{
    if (colorspec->type == TC_LT)
	term->linetype(colorspec->lt);
}

Project: gnuplot

term_check_multiplot_okay(TBOOLEAN f_interactive)
{
    FPRINTF((stderr, "term_multiplot_okay(%d)\n", f_interactive));

    if (!term_initialised)
	return;                 /* they've not started yet */

    /* make sure that it is safe to issue an interactive prompt
     * it is safe if
     *   it is not an interactive read, or
     *   the terminal supports interactive multiplot, or
     *   we are not writing to stdout and terminal doesn't
     *     refuse multiplot outright
     */
    if (!f_interactive || (term->flags & TERM_CAN_MULTIPLOT) ||
	((gpoutfile != stdout) && !(term->flags & TERM_CANNOT_MULTIPLOT))
	) {
	/* it's okay to use multiplot here, but suspend first */
	term_suspend();
	return;
    }
    /* uh oh: they're not allowed to be in multiplot here */

    term_end_multiplot();

    /* at this point we know that it is interactive and that the
     * terminal can either only do multiplot when writing to
     * to a file, or it does not do multiplot at all
     */

    if (term->flags & TERM_CANNOT_MULTIPLOT)
	int_error(NO_CARET, "This terminal does not support multiplot");
    else
	int_error(NO_CARET, "Must set output to a file or put all multiplot commands on one input line");
}

Project: gnuplot

check_for_mouse_events()
{
#ifdef USE_MOUSE
    if (term_initialised && term->waitforinput) {
	term->waitforinput(TERM_ONLY_CHECK_MOUSING);
    }
#endif
#ifdef _WIN32
    /* Process windows GUI events (e.g. for text window, or wxt and windows terminals) */
    WinMessageLoop();
    /* On Windows, Ctrl-C only sets this flag. */
    /* The next block duplicates the behaviour of inter(). */
    if (ctrlc_flag) {
	ctrlc_flag = FALSE;
	term_reset();
	putc('\n', stderr);
	fprintf(stderr, "Ctrl-C detected!\n");
	bail_to_command_line();	/* return to prompt */
    }
#endif
}

Project: gnuplot

term_start_plot()
{
    FPRINTF((stderr, "term_start_plot()\n"));

    if (!term_initialised)
	term_initialise();

    if (!term_graphics) {
	FPRINTF((stderr, "- calling term->graphics()\n"));
	(*term->graphics) ();
	term_graphics = TRUE;
    } else if (multiplot && term_suspended) {
	if (term->resume) {
	    FPRINTF((stderr, "- calling term->resume()\n"));
	    (*term->resume) ();
	}
	term_suspended = FALSE;
    }

    if (multiplot)
	multiplot_count++;

    /* Sync point for epslatex text positioning */
    (*term->layer)(TERM_LAYER_RESET);

    /* Because PostScript plots may be viewed out of order, make sure */
    /* Each new plot makes no assumption about the previous palette.  */
    if (term->flags & TERM_IS_POSTSCRIPT)
	invalidate_palette();

    /* Set canvas size to full range of current terminal coordinates */
	canvas.xleft  = 0;
	canvas.xright = term->xmax - 1;
	canvas.ybot   = 0;
	canvas.ytop   = term->ymax - 1;

}

Project: gnuplot

term_reset()
{
    FPRINTF((stderr, "term_reset()\n"));

#ifdef USE_MOUSE
    /* Make sure that ^C will break out of a wait for 'pause mouse' */
    paused_for_mouse = 0;
#ifdef _WIN32
    kill_pending_Pause_dialog();
#endif
#endif

    if (!term_initialised)
	return;

    if (term_suspended) {
	if (term->resume) {
	    FPRINTF((stderr, "- calling term->resume()\n"));
	    (*term->resume) ();
	}
	term_suspended = FALSE;
    }
    if (term_graphics) {
	(*term->text) ();
	term_graphics = FALSE;
    }
    if (term_initialised) {
	(*term->reset) ();
	term_initialised = FALSE;
	/* switch off output to special postscript file (if used) */
	gppsfile = NULL;
    }
}

Project: gnuplot

do_point(unsigned int x, unsigned int y, int number)
{
    int htic, vtic;
    struct termentry *t = term;

    /* use solid lines for point symbols */
    if (term->dashtype != null_dashtype)
	term->dashtype(DASHTYPE_SOLID, NULL);

    if (number < 0) {           /* do dot */
	(*t->move) (x, y);
	(*t->vector) (x, y);
	return;
    }
    number %= POINT_TYPES;
    /* should be in term_tbl[] in later version */
    htic = (term_pointsize * t->h_tic / 2);
    vtic = (term_pointsize * t->v_tic / 2);

    /* point types 1..4 are same as in postscript, png and x11
       point types 5..6 are "similar"
       (note that (number) equals (pointtype-1)
    */
    switch (number) {
    case 4:                     /* do diamond */
	(*t->move) (x - htic, y);
	(*t->vector) (x, y - vtic);
	(*t->vector) (x + htic, y);
	(*t->vector) (x, y + vtic);
	(*t->vector) (x - htic, y);
	(*t->move) (x, y);
	(*t->vector) (x, y);
	break;
    case 0:                     /* do plus */
	(*t->move) (x - htic, y);
	(*t->vector) (x - htic, y);
	(*t->vector) (x + htic, y);
	(*t->move) (x, y - vtic);
	(*t->vector) (x, y - vtic);
	(*t->vector) (x, y + vtic);
	break;
    case 3:                     /* do box */
	(*t->move) (x - htic, y - vtic);
	(*t->vector) (x - htic, y - vtic);
	(*t->vector) (x + htic, y - vtic);
	(*t->vector) (x + htic, y + vtic);
	(*t->vector) (x - htic, y + vtic);
	(*t->vector) (x - htic, y - vtic);
	(*t->move) (x, y);
	(*t->vector) (x, y);
	break;
    case 1:                     /* do X */
	(*t->move) (x - htic, y - vtic);
	(*t->vector) (x - htic, y - vtic);
	(*t->vector) (x + htic, y + vtic);
	(*t->move) (x - htic, y + vtic);
	(*t->vector) (x - htic, y + vtic);
	(*t->vector) (x + htic, y - vtic);
	break;
    case 5:                     /* do triangle */
	(*t->move) (x, y + (4 * vtic / 3));
	(*t->vector) (x - (4 * htic / 3), y - (2 * vtic / 3));
	(*t->vector) (x + (4 * htic / 3), y - (2 * vtic / 3));
	(*t->vector) (x, y + (4 * vtic / 3));
	(*t->move) (x, y);
	(*t->vector) (x, y);
	break;
    case 2:                     /* do star */
	(*t->move) (x - htic, y);
	(*t->vector) (x - htic, y);
	(*t->vector) (x + htic, y);
	(*t->move) (x, y - vtic);
	(*t->vector) (x, y - vtic);
	(*t->vector) (x, y + vtic);
	(*t->move) (x - htic, y - vtic);
	(*t->vector) (x - htic, y - vtic);
	(*t->vector) (x + htic, y + vtic);
	(*t->move) (x - htic, y + vtic);
	(*t->vector) (x - htic, y + vtic);
	(*t->vector) (x + htic, y - vtic);
	break;
    }
}

Project: gnuplot

MOVE_null(unsigned int x, unsigned int y)
{
    (void) x;                   /* avoid -Wunused warning */
    (void) y;
}

Project: gnuplot

UNKNOWN_null()
{
}

Project: gnuplot

load_linetype(struct lp_style_type *lp, int tag)
{
    struct linestyle_def *this;
    TBOOLEAN recycled = FALSE;

recycle:

    if ((tag > 0)
    && (monochrome || (term && (term->flags & TERM_MONOCHROME)))) {
	for (this = first_mono_linestyle; this; this = this->next) {
	    if (tag == this->tag) {
		*lp = this->lp_properties;
		return;
	    }
	}

	/* This linetype wasn't defined explicitly.		*/
	/* Should we recycle one of the first N linetypes?	*/
	if (tag > mono_recycle_count && mono_recycle_count > 0) {
	    tag = (tag-1) % mono_recycle_count + 1;
	    goto recycle;
	}

	return;
    }

    this = first_perm_linestyle;
    while (this != NULL) {
	if (this->tag == tag) {
	    /* Always load color, width, and dash properties */
	    lp->l_type = this->lp_properties.l_type;
	    lp->l_width = this->lp_properties.l_width;
	    lp->pm3d_color = this->lp_properties.pm3d_color;
	    lp->d_type = this->lp_properties.d_type;
	    lp->custom_dash_pattern = this->lp_properties.custom_dash_pattern;

	    /* Needed in version 5.0 to handle old terminals (pbm hpgl ...) */
	    /* with no support for user-specified colors */
	    if (term && term->set_color == null_set_color)
		lp->l_type = tag;

	    /* Do not recycle point properties. */
	    /* FIXME: there should be a separate command "set pointtype cycle N" */
	    if (!recycled) {
	    	lp->p_type = this->lp_properties.p_type;
	    	lp->p_interval = this->lp_properties.p_interval;
	    	lp->p_size = this->lp_properties.p_size;
	    	memcpy(lp->p_char, this->lp_properties.p_char, sizeof(lp->p_char));
	    }
	    return;
	} else {
	    this = this->next;
	}
    }

    /* This linetype wasn't defined explicitly.		*/
    /* Should we recycle one of the first N linetypes?	*/
    if (tag > linetype_recycle_count && linetype_recycle_count > 0) {
	tag = (tag-1) % linetype_recycle_count + 1;
	recycled = TRUE;
	goto recycle;
    }

    /* No user-defined linetype with this tag; fall back to default line type. */
    /* NB: We assume that the remaining fields of lp have been initialized. */
    lp->l_type = tag - 1;
    lp->pm3d_color.type = TC_LT;
    lp->pm3d_color.lt = lp->l_type;
    lp->d_type = DASHTYPE_SOLID;
    lp->p_type = (tag <= 0) ? -1 : tag - 1;
}

Project: gnuplot

term_end_plot()
{
    FPRINTF((stderr, "term_end_plot()\n"));

    if (!term_initialised)
	return;

    /* Sync point for epslatex text positioning */
    (*term->layer)(TERM_LAYER_END_TEXT);

    if (!multiplot) {
	FPRINTF((stderr, "- calling term->text()\n"));
	(*term->text) ();
	term_graphics = FALSE;
    } else {
	multiplot_next();
    }

#ifdef VMS
    if (opened_binary)
	fflush_binary();
    else
#endif /* VMS */
	(void) fflush(gpoutfile);

#ifdef USE_MOUSE
    if (term->set_ruler) {
	recalc_statusline();
	update_ruler();
    }
#endif
}

Project: gnuplot

LINETYPE_null(int t)
{
    (void) t;                   /* avoid -Wunused warning */
}

Project: gnuplot

PUTTEXT_null(unsigned int x, unsigned int y, const char *s)
{
    (void) s;                   /* avoid -Wunused warning */
    (void) x;
    (void) y;
}

Project: gnuplot

null_set_font(const char *font)
{
    (void) font;		/* avoid -Wunused warning */
    return FALSE;		/* Never used!! */
}

Project: gnuplot

init_monochrome()
{
    struct lp_style_type mono_default[] = DEFAULT_MONO_LINETYPES;

    if (first_mono_linestyle == NULL) {
	int i, n = sizeof(mono_default) / sizeof(struct lp_style_type);
	struct linestyle_def *new;
	/* copy default list into active list */
	for (i=n; i>0; i--) {
	    new = gp_alloc(sizeof(struct linestyle_def), NULL);
	    new->next = first_mono_linestyle;
	    new->lp_properties = mono_default[i-1];
	    new->tag = i;
	    first_mono_linestyle = new;
	}
    }
}

Project: gnuplot

set_term()
{
    struct termentry *t = NULL;
    char *input_name = NULL;

    if (!END_OF_COMMAND) {
	input_name = gp_input_line + token[c_token].start_index;
	t = change_term(input_name, token[c_token].length);
	if (!t && isstringvalue(c_token) && (input_name = try_to_get_string())) {
	    if (strchr(input_name, ' '))
		*strchr(input_name, ' ') = '\0';
	    t = change_term(input_name, strlen(input_name));
	    free(input_name);
	} else {
	    c_token++;
	}
    }

    if (!t) {
	change_term("unknown", 7);
	int_error(c_token-1, "unknown or ambiguous terminal type; type just 'set terminal' for a list");
    }

    /* otherwise the type was changed */
    return (t);
}

Project: gnuplot

estimate_plaintext(char *enhancedtext)
{
    if (enhancedtext == NULL)
	return NULL;
    estimate_strlen(enhancedtext, NULL);
    return ENHest_plaintext;
}

Project: gnuplot

null_scale(double x, double y)
{
    (void) x;                   /* avoid -Wunused warning */
    (void) y;
    int_error(NO_CARET, "Attempt to call deprecated terminal function");
    return FALSE;               /* can't be done */
}

Project: gnuplot

do_enh_writec(int c)
{
    /* note: c is meant to hold a char, but is actually an int, for
     * the same reasons applying to putc() and friends */
    *enhanced_cur_text++ = c;
}

Project: gnuplot

null_text_angle(int ang)
{
    return (ang == 0);
}

Project: gnuplot

load_dashtype(struct t_dashtype *dt, int tag)
{
    struct custom_dashtype_def *this;
    struct t_dashtype loc_dt = DEFAULT_DASHPATTERN;

    this = first_custom_dashtype;
    while (this != NULL) {
	if (this->tag == tag) {
	    *dt = this->dashtype;
	    memcpy(dt->dstring, this->dashtype.dstring, sizeof(dt->dstring));
	    return this->d_type;
	} else {
	    this = this->next;
	}
    }

    /* not found, fall back to default, terminal-dependent dashtype */
    *dt = loc_dt;
    return tag - 1;
}

Project: gnuplot

term_initialise()
{
    FPRINTF((stderr, "term_initialise()\n"));

    if (!term)
	int_error(NO_CARET, "No terminal defined");

    /* check if we have opened the output file in the wrong mode
     * (text/binary), if set term comes after set output
     * This was originally done in change_term, but that
     * resulted in output files being truncated
     */

    if (outstr && (term->flags & TERM_NO_OUTPUTFILE)) {
	if (interactive)
	    fprintf(stderr,"Closing %s\n",outstr);
	term_close_output();
    }

    if (outstr &&
	(((term->flags & TERM_BINARY) && !opened_binary) ||
	 ((!(term->flags & TERM_BINARY) && opened_binary)))) {
	/* this is nasty - we cannot just term_set_output(outstr)
	 * since term_set_output will first free outstr and we
	 * end up with an invalid pointer. I think I would
	 * prefer to defer opening output file until first plot.
	 */
	char *temp = gp_alloc(strlen(outstr) + 1, "temp file string");
	if (temp) {
	    FPRINTF((stderr, "term_initialise: reopening \"%s\" as %s\n",
		     outstr, term->flags & TERM_BINARY ? "binary" : "text"));
	    strcpy(temp, outstr);
	    term_set_output(temp);      /* will free outstr */
	    if (temp != outstr) {
		if (temp)
		    free(temp);
		temp = outstr;
	    }
	} else
	    fputs("Cannot reopen output file in binary", stderr);
	/* and carry on, hoping for the best ! */
    }
#if defined(MSDOS) || defined (_WIN32) || defined(OS2)
# ifdef _WIN32
    else if (!outstr && (term->flags & TERM_BINARY))
# else
    else if (!outstr && !interactive && (term->flags & TERM_BINARY))
# endif
	{
#if defined(_WIN32) && !defined(WGP_CONSOLE)
#ifdef PIPES
	    if (!output_pipe_open)
#endif
		if (outstr == NULL && !(term->flags & TERM_NO_OUTPUTFILE))
		    int_error(c_token, "cannot output binary data to wgnuplot text window");
#endif
	    /* binary to stdout in non-interactive session... */
	    fflush(stdout);
	    setmode(fileno(stdout), O_BINARY);
	}
#endif

    if (!term_initialised || term_force_init) {
	FPRINTF((stderr, "- calling term->init()\n"));
	(*term->init) ();
	term_initialised = TRUE;
#ifdef HAVE_LOCALE_H
	/* This is here only from an abundance of caution (a.k.a. paranoia).
	 * Some terminals (wxt qt caca) are known to change the locale when
	 * initialized.  Others have been implicated (gd).  Rather than trying
	 * to catch all such offenders one by one, cover for all of them here.
	 */
	setlocale(LC_NUMERIC, "C");
#endif
    }
}

Project: gnuplot

do_pointsize(double size)
{
    term_pointsize = (size >= 0 ? size : 1);
}

Project: gnuplot

get_terminals_names()
{
    int i;
    char *buf = gp_alloc(TERMCOUNT*15, "all_term_names"); /* max 15 chars per name */
    char *names;
    int sort_idxs[TERMCOUNT];

    /* sort terminal types alphabetically */
    for( i = 0; i < TERMCOUNT; i++ )
	sort_idxs[i] = i;
    qsort( sort_idxs, TERMCOUNT, sizeof(int), termcomp );
    /* now sort_idxs[] contains the sorted indices */

    strcpy(buf, " "); /* let the string have leading and trailing " " in order to search via strstrt(GPVAL_TERMINALS, " png "); */
    for (i = 0; i < TERMCOUNT; i++)
	sprintf(buf+strlen(buf), "%s ", term_tbl[sort_idxs[i]].name);
    names = gp_alloc(strlen(buf)+1, "all_term_names2");
    strcpy(names, buf);
    free(buf);
    return names;
}

Project: gnuplot

change_term(const char *origname, int length)
{
    int i;
    struct termentry *t = NULL;
    TBOOLEAN ambiguous = FALSE;

    /* For backwards compatibility only */
    char *name = (char *)origname;
    if (!strncmp(origname,"X11",length)) {
	name = "x11";
	length = 3;
    }

#ifdef HAVE_CAIROPDF
    /* To allow "set term eps" as short for "set term epscairo" */
    if (!strncmp(origname,"eps",length)) {
	name = "epscairo";
	length = 8;
    }
#endif
#ifdef HAVE_LIBGD
    /* To allow "set term sixel" as short for "set term sixelgd" */
    if (!strncmp(origname, "sixel", length)) {
	name = "sixelgd";
	length = 7;
    }
#endif

    for (i = 0; i < TERMCOUNT; i++) {
	if (!strncmp(name, term_tbl[i].name, length)) {
	    if (t)
		ambiguous = TRUE;
	    t = term_tbl + i;
	    /* Exact match is always accepted */
	    if (length == strlen(term_tbl[i].name)) {
		ambiguous = FALSE;
		break;
	    }
	}
    }

    if (!t || ambiguous)
	return (NULL);

    /* Success: set terminal type now */

    term = t;
    term_initialised = FALSE;

    /* check that optional fields are initialised to something */
    if (term->text_angle == 0)
	term->text_angle = null_text_angle;
    if (term->justify_text == 0)
	term->justify_text = null_justify_text;
    if (term->point == 0)
	term->point = do_point;
    if (term->arrow == 0)
	term->arrow = do_arrow;
    if (term->pointsize == 0)
	term->pointsize = do_pointsize;
    if (term->linewidth == 0)
	term->linewidth = null_linewidth;
    if (term->layer == 0)
	term->layer = null_layer;
    if (term->tscale <= 0)
	term->tscale = 1.0;
    if (term->set_font == 0)
	term->set_font = null_set_font;
    if (term->set_color == 0) {
	term->set_color = null_set_color;
	term->flags |= TERM_NULL_SET_COLOR;
    }
    if (term->dashtype == 0)
	term->dashtype = null_dashtype;

    if (interactive)
	fprintf(stderr, "\nTerminal type is now '%s'\n", term->name);

    /* Invalidate any terminal-specific structures that may be active */
    invalidate_palette();

    return (t);
}

Project: gnuplot

term_close_output()
{
    FPRINTF((stderr, "term_close_output\n"));

    opened_binary = FALSE;

    if (!outstr)                /* ie using stdout */
	return;

#if defined(PIPES)
    if (output_pipe_open) {
	(void) pclose(gpoutfile);
	output_pipe_open = FALSE;
    } else
#endif /* PIPES */
#ifdef _WIN32
    if (stricmp(outstr, "PRN") == 0)
	close_printer(gpoutfile);
    else
#endif
    if (gpoutfile != gppsfile)
	fclose(gpoutfile);

    gpoutfile = stdout;         /* Don't dup... */
    free(outstr);
    outstr = NULL;

    if (gppsfile)
	fclose(gppsfile);
    gppsfile = NULL;
}

Project: gnuplot

options_null()
{
    term_options[0] = '\0';     /* we have no options */
}

Project: gnuplot

do_arc(
    int cx, int cy, /* Center */
    double radius, /* Radius */
    double arc_start, double arc_end, /* Limits of arc in degrees */
    int style, TBOOLEAN wedge)
{
    gpiPoint vertex[250];
    int i, segments;
    double aspect;
    TBOOLEAN complete_circle;

    /* Protect against out-of-range values */
    while (arc_start < 0)
	arc_start += 360.;
    while (arc_end > 360.)
	arc_end -= 360.;

    /* Always draw counterclockwise */
    while (arc_end < arc_start)
	arc_end += 360.;

    /* Choose how finely to divide this arc into segments */
    /* Note: INC=2 caused problems for gnuplot_x11 */
#   define INC 3.
    segments = (arc_end - arc_start) / INC;
    if (segments < 1)
	segments = 1;

    /* Calculate the vertices */
    aspect = (double)term->v_tic / (double)term->h_tic;
    for (i=0; i<segments; i++) {
	vertex[i].x = cx + cos(DEG2RAD * (arc_start + i*INC)) * radius;
	vertex[i].y = cy + sin(DEG2RAD * (arc_start + i*INC)) * radius * aspect;
    }
#   undef INC
    vertex[segments].x = cx + cos(DEG2RAD * arc_end) * radius;
    vertex[segments].y = cy + sin(DEG2RAD * arc_end) * radius * aspect;

    if (fabs(arc_end - arc_start) > .1
    &&  fabs(arc_end - arc_start) < 359.9) {
	vertex[++segments].x = cx;
	vertex[segments].y = cy;
	vertex[++segments].x = vertex[0].x;
	vertex[segments].y = vertex[0].y;
	complete_circle = FALSE;
    } else
	complete_circle = TRUE;

    if (style) { /* Fill in the center */
	gpiPoint fillarea[250];
	int in;

	clip_polygon(vertex, fillarea, segments, &in);
	fillarea[0].style = style;
	if (term->filled_polygon)
	    term->filled_polygon(in, fillarea);

    } else { /* Draw the arc */
	if (!wedge && !complete_circle)
	    segments -= 2;
	draw_clip_polygon(segments+1, vertex);
    }
}

Project: gnuplot

enh_err_check(const char *str)
{
    if (*str == '}')
	int_warn(NO_CARET, "enhanced text mode parser - ignoring spurious }");
    else
	int_warn(NO_CARET, "enhanced text mode parsing error");
}

Project: gnuplot

do_arrow(
    unsigned int usx, unsigned int usy,   /* start point */
    unsigned int uex, unsigned int uey,   /* end point (point of arrowhead) */
    int headstyle)
{
    /* Clipping and angle calculations do not work if coords are unsigned! */
    int sx = (int)usx;
    int sy = (int)usy;
    int ex = (int)uex;
    int ey = (int)uey;

    struct termentry *t = term;
    double len_tic = ((double) (t->h_tic + t->v_tic)) / 2.0;
    /* average of tic sizes */
    /* (dx,dy) : vector from end to start */
    double dx = sx - ex;
    double dy = sy - ey;
    double len_arrow = sqrt(dx * dx + dy * dy);
    gpiPoint head_points[5];
    int xm = 0, ym = 0;
    BoundingBox *clip_save;

    /* The arrow shaft was clipped already in draw_clip_arrow() but we still */
    /* need to clip the head here. */
    clip_save = clip_area;
    if (term->flags & TERM_CAN_CLIP)
	clip_area = NULL;
    else
	clip_area = &canvas;

    /* Calculate and draw arrow heads.
     * Draw no head for arrows with length = 0, or, to be more specific,
     * length < DBL_EPSILON, because len_arrow will almost always be != 0.
     */
    if ((headstyle & BOTH_HEADS) != NOHEAD && fabs(len_arrow) >= DBL_EPSILON) {
	int x1, y1, x2, y2;
	if (curr_arrow_headlength <= 0) {
	    /* An arrow head with the default size and angles */
	    double coeff_shortest = len_tic * HEAD_SHORT_LIMIT / len_arrow;
	    double coeff_longest = len_tic * HEAD_LONG_LIMIT / len_arrow;
	    double head_coeff = GPMAX(coeff_shortest,
				      GPMIN(HEAD_COEFF, coeff_longest));
	    /* we put the arrowhead marks at 15 degrees to line */
	    x1 = (int) ((COS15 * dx - SIN15 * dy) * head_coeff);
	    y1 = (int) ((SIN15 * dx + COS15 * dy) * head_coeff);
	    x2 = (int) ((COS15 * dx + SIN15 * dy) * head_coeff);
	    y2 = (int) ((-SIN15 * dx + COS15 * dy) * head_coeff);
	    /* backangle defaults to 90 deg */
	    xm = (int) ((x1 + x2)/2);
	    ym = (int) ((y1 + y2)/2);
	} else {
	    /* An arrow head with the length + angle specified explicitly.	*/
	    /* Assume that if the arrow is shorter than the arrowhead, this is	*/
	    /* because of foreshortening in a 3D plot.                  	*/
	    double alpha = curr_arrow_headangle * DEG2RAD;
	    double beta = curr_arrow_headbackangle * DEG2RAD;
	    double phi = atan2(-dy,-dx); /* azimuthal angle of the vector */
	    double backlen, effective_length;
	    double dx2, dy2;

	    effective_length = curr_arrow_headlength;
	    if (!curr_arrow_headfixedsize && (curr_arrow_headlength > len_arrow/2.)) {
		effective_length = len_arrow/2.;
		alpha = atan(tan(alpha)*((double)curr_arrow_headlength/effective_length));
		beta = atan(tan(beta)*((double)curr_arrow_headlength/effective_length));
	    }
	    backlen = sin(alpha) / sin(beta);

	    /* anticlock-wise head segment */
	    x1 = -(int)(effective_length * cos( alpha - phi ));
	    y1 =  (int)(effective_length * sin( alpha - phi ));
	    /* clock-wise head segment */
	    dx2 = -effective_length * cos( phi + alpha );
	    dy2 = -effective_length * sin( phi + alpha );
	    x2 = (int) (dx2);
	    y2 = (int) (dy2);
	    /* back point */
	    xm = (int) (dx2 + backlen*effective_length * cos( phi + beta ));
	    ym = (int) (dy2 + backlen*effective_length * sin( phi + beta ));
	}

	if ((headstyle & END_HEAD) && !clip_point(ex, ey)) {
	    head_points[0].x = ex + xm;
	    head_points[0].y = ey + ym;
	    head_points[1].x = ex + x1;
	    head_points[1].y = ey + y1;
	    head_points[2].x = ex;
	    head_points[2].y = ey;
	    head_points[3].x = ex + x2;
	    head_points[3].y = ey + y2;
	    head_points[4].x = ex + xm;
	    head_points[4].y = ey + ym;
	    if (!((headstyle & SHAFT_ONLY))) {
		if (curr_arrow_headfilled >= AS_FILLED) {
		    /* draw filled forward arrow head */
		    head_points->style = FS_OPAQUE;
		    if (t->filled_polygon)
			(*t->filled_polygon) (5, head_points);
		}
		/* draw outline of forward arrow head */
		if (curr_arrow_headfilled == AS_NOFILL) {
		    draw_clip_polygon(3, head_points+1);
		} else if (curr_arrow_headfilled != AS_NOBORDER) {
		    draw_clip_polygon(5, head_points);
		}
	    }
	}

	/* backward arrow head */
	if ((headstyle & BACKHEAD) && !clip_point(sx,sy)) {
	    head_points[0].x = sx - xm;
	    head_points[0].y = sy - ym;
	    head_points[1].x = sx - x1;
	    head_points[1].y = sy - y1;
	    head_points[2].x = sx;
	    head_points[2].y = sy;
	    head_points[3].x = sx - x2;
	    head_points[3].y = sy - y2;
	    head_points[4].x = sx - xm;
	    head_points[4].y = sy - ym;
	    if (!((headstyle & SHAFT_ONLY))) {
		if (curr_arrow_headfilled >= AS_FILLED) {
		    /* draw filled backward arrow head */
		    head_points->style = FS_OPAQUE;
		    if (t->filled_polygon)
			(*t->filled_polygon) (5, head_points);
		}
		/* draw outline of backward arrow head */
		if (curr_arrow_headfilled == AS_NOFILL) {
		    draw_clip_polygon(3, head_points+1);
		} else if (curr_arrow_headfilled != AS_NOBORDER) {
		    draw_clip_polygon(5, head_points);
		}
	    }
	}
    }

    /* Adjust the length of the shaft so that it doesn't overlap the head */
    if ((headstyle & BACKHEAD)
    &&  (fabs(len_arrow) >= DBL_EPSILON) && (curr_arrow_headfilled != AS_NOFILL) ) {
	sx -= xm;
	sy -= ym;
    }
    if ((headstyle & END_HEAD)
    &&  (fabs(len_arrow) >= DBL_EPSILON) && (curr_arrow_headfilled != AS_NOFILL) ) {
	ex += xm;
	ey += ym;
    }

    /* Draw the line for the arrow. */
    if (!((headstyle & HEADS_ONLY)))
	draw_clip_line(sx, sy, ex, ey);

    /* Restore previous clipping box */
    clip_area = clip_save;
}

Project: gnuplot

null_layer(t_termlayer layer)
{
    (void) layer;               /* avoid -Wunused warning */
}