/*
 * TransFig: Facility for Translating Fig code
 * Copyright (c) 1985 Supoj Sutantavibul
 * Copyright (c) 1991 Micah Beck
 *
 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * The X Consortium, and any party obtaining a copy of these files from
 * the X Consortium, directly or indirectly, is granted, free of charge, a
 * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
 * nonexclusive right and license to deal in this software and
 * documentation files (the "Software"), including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons who receive
 * copies from any such party to do so, with the only requirement being
 * that this copyright notice remain intact.  This license includes without
 * limitation a license to do the foregoing actions under any patents of
 * the party supplying this software to the X Consortium.
 */

/* 
 *	FIG : Facility for Interactive Generation of figures
 *
 *	Copyright (c) 1985, 1988 by Supoj Sutanthavibul (supoj@sally.UTEXAS.EDU)
 *	January 1985.
 *	1st revision : August 1985.
 *	2nd revision : March 1988.
 *
 *	%W%	%G%
*/
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include "alloc.h"
#include "fig2dev.h"
#include "object.h"
#include "../patchlevel.h"

#if defined(hpux) || defined(SYSV) || defined(SVR4)
#define bzero(s,n) memset((s),'\0',(n))
#endif

extern int            errno;

extern F_arrow		*make_arrow();
extern char		*calloc();
extern double		 floor(), ceil();

static void		 read_colordef();
static F_ellipse	*read_ellipseobject();
static F_line		*read_lineobject();
static F_text		*read_textobject();
static F_spline		*read_splineobject();
static F_arc		*read_arcobject();
static F_compound	*read_compoundobject();

#define			FILL_CONVERT(f) \
				((v2_flag || (f) < WHITE_FILL) \
					? (v30_flag? f: (f-1)) : 20 - ((f)-1)*5)

#define			BUF_SIZE		1024

User_color		user_colors[MAX_USR_COLS];
int			user_col_indx[MAX_USR_COLS];
int			num_usr_cols;
char			buf[BUF_SIZE];
int			line_no = 0;
int			num_object;
int			v2_flag;	/* Protocol V2.0 or higher */
int			v21_flag;	/* Protocol V2.1 or higher */
int			v30_flag;	/* Protocol V3.0 or higher */
float			THICK_SCALE;	/* convert line thickness from screen res. */

read_fail_message(file, err)
char	*file;
int	err;
{
	extern char	*sys_errlist[];

	if (err == 0)		/* Successful read */
	    return;
#if !defined(hpux) && !defined(SYSV) && !defined(SVR4)
	else if (err == ENAMETOOLONG)
	    put_msg("File name \"%s\" is too long", file);
#endif
	else if (err == ENOENT)
	    put_msg("File \"%s\" does not exist", file);
	else if (err == ENOTDIR)
	    put_msg("A name in the path \"%s\" is not a directory", file);
	else if (err == EACCES)
	    put_msg("Read access to file \"%s\" is blocked", file);
	else if (err == EISDIR)
	    put_msg("File \"%s\" is a directory", file);
	else if (err == -2) {
	    put_msg("File \"%s\" is empty", file);
	    }
	else if (err == -1) {
	    /* Format error; relevant error message is already delivered */
	    }
	else
	    put_msg("File \"%s\" is not accessable; %s", file, sys_errlist[err]);
	}

/**********************************************************
Read_fig returns :

     0 : successful read.
    -1 : File is in incorrect format
    -2 : File is empty
err_no : if file can not be read for various reasons

The resolution (ppi) and the cooridnate system (coord_sys) are
stored in obj->nwcorner.x and obj->nwcorner.x respectively.
**********************************************************/

read_fig(file_name, obj)
char		*file_name;
F_compound	*obj;
{
	FILE		*fp;

	init_pats_used();
	if ((fp = fopen(file_name, "r")) == NULL)
	    return(errno);
	else
	    return(readfp_fig(fp, obj));
	}

readfp_fig(fp, obj)
FILE	*fp;
F_compound	*obj;
{
	char		c;
	int		status;

	num_object = 0;
	num_usr_cols = 0;
	c = fgetc(fp);
	if (feof(fp)) return(-2);
	ungetc(c, fp);
	bzero((char*)obj, COMOBJ_SIZE);
	if (c == '#')
	    status = read_objects(fp, obj);
	else
	    status = read_1_3_objects(fp, obj);
	(void)fclose(fp);
	return(status);
	}
	
int
read_objects(fp, obj)
FILE		*fp;
F_compound	*obj;
{
	F_ellipse	*e, *le = NULL;
	F_line		*l, *ll = NULL;
	F_text		*t, *lt = NULL;
	F_spline	*s, *ls = NULL;
	F_arc		*a, *la = NULL;
	F_compound	*c, *lc = NULL;
	int		object, ppi, coord_sys;

	bzero((char*)obj, COMOBJ_SIZE);
	(void)fgets(buf, BUF_SIZE, fp);	/* get the version line */
	if (strlen(buf) > 0)
	    buf[strlen(buf)-1] = '\0';	/* remove newline */

	/* v2_flag is for version 2 or higher */
	v2_flag = (!strncmp(buf, "#FIG 2", 6) || !strncmp(buf, "#FIG 3", 6));
	/* v21_flag is for version 2.1 or higher */
	v21_flag = (!strncmp(buf, "#FIG 2.1", 8) || !strncmp(buf, "#FIG 3", 6));
	/* version 2.2 was only beta - 3.0 is the official release (they are identical) */
	v30_flag = (!strncmp(buf, "#FIG 3", 6) || !strncmp(buf, "#FIG 2.2", 8));
	if (strncmp(&buf[5],VERSION,3) > 0) {
	    put_msg("Fig file format (%s) newer than this version of fig2dev (%s), exiting",
			&buf[5],VERSION);
	    exit(1);
	}

	if (v30_flag) {
	    /* read the orientation spec (landscape/portrait) */
	    line_no++;
	    if (get_line(fp) < 0) {
		put_msg("File is truncated at landscape/portrait specification.");
		return(-1);
	    }
	    /* but set only of the user didn't specify the orientation
	       on the command line */
	    if (!orientspec)
		landscape = !strncasecmp(buf,"land",4);

	    /* now read the metric/inches spec OR centering spec */
	    line_no++;
	    if (get_line(fp) < 0) {
		put_msg("File is truncated at metric/inches or centering specification.");
		return(-1);
	    }
	    if ((strncasecmp(buf,"center",6) == 0) || 
		(strncasecmp(buf,"flush",5) == 0)) {
		center = strncasecmp(buf,"flush",5);
		/* now read metric/inches spec */
		line_no++;
		if (get_line(fp) < 0) {
		    put_msg("File is truncated at metric/inches specification.");
		    return(-1);
		}
	    }
	    /* if metric, scale magnification to correct for xfig display error */
	    if (strncasecmp(buf,"metric",5)==0)
		mag *= 80.0/76.2;
	}

	line_no++;
	/* now read for resolution and coord_sys */
	if (get_line(fp) < 0) {
	    put_msg("File is truncated at resolution specification.");
	    return(-1);
	    }
	if (sscanf(buf,"%d%d\n", &ppi, &coord_sys) != 2) {
	    put_msg("Incomplete resolution information at line %d", line_no);
	    return(-1);
	    }

	THICK_SCALE = ppi/80;	/* convert line thickness from screen resolution */
	obj->nwcorner.x = ppi;
	obj->nwcorner.y = coord_sys;
	while (get_line(fp) > 0) {
	    if (sscanf(buf, "%d", &object) != 1) {
		put_msg("Incorrect format at line %d", line_no);
		return(-1);
		}
	    switch (object) {
		case O_COLOR_DEF:
		    read_colordef(fp);
		    if (num_object) {
			put_msg("Color definitions must come before other objects (line %d).",
				line_no);
			return (-1);
		    }
		    num_usr_cols++;
		    break;
		case O_POLYLINE :
		    if ((l = read_lineobject(fp)) == NULL) 
			return(-1);
		    if (ll)
			ll = (ll->next = l);
		    else 
			ll = obj->lines = l;
		    num_object++;
		    break;
		case O_SPLINE :
		    if ((s = read_splineobject(fp)) == NULL) 
			return(-1);
		    if (ls)
			ls = (ls->next = s);
		    else 
			ls = obj->splines = s;
		    num_object++;
		    break;
		case O_ELLIPSE :
		    if ((e = read_ellipseobject()) == NULL) 
			return(-1);
		    if (le)
			le = (le->next = e);
		    else 
			le = obj->ellipses = e;
		    num_object++;
		    break;
		case O_ARC :
		    if ((a = read_arcobject(fp)) == NULL) 
			return(-1);
		    if (la)
			la = (la->next = a);
		    else 
			la = obj->arcs = a;
		    num_object++;
		    break;
		case O_TEXT :
		    if ((t = read_textobject(fp)) == NULL) 
			return(-1);
		    if (lt)
			lt = (lt->next = t);
		    else 
			lt = obj->texts = t;
		    num_object++;
		    break;
		case O_COMPOUND :
		    if ((c = read_compoundobject(fp)) == NULL) 
			return(-1);
		    if (lc)
			lc = (lc->next = c);
		    else 
			lc = obj->compounds = c;
		    num_object++;
		    break;
		default :
		    put_msg("Incorrect object code at line %d", line_no);
		    return(-1);
		} /*  switch */
	    } /*  while */
	if (feof(fp))
	    return(0);
	else
	    return(errno);
	} /*  read_objects */

static void
read_colordef(fp)
    FILE	   *fp;
{ 
    int		    c,r,g,b;

    if ((sscanf(buf, "%*d %d #%02x%02x%02x", &c, &r, &g, &b) != 4) ||
		(c < NUM_STD_COLS)) {
	buf[strlen(buf)-1]='\0';	/* remove the newline */
	put_msg("Invalid color definition: %s, setting to black (#00000).",buf);
	r=g=b=0;
    }
    user_col_indx[num_usr_cols] = c;
    user_colors[num_usr_cols].r = r;
    user_colors[num_usr_cols].g = g;
    user_colors[num_usr_cols].b = b;
}

fix_color(color)
    int		    *color;
{
    int		    i;
    if (*color < NUM_STD_COLS)
	return;
    for (i=0; i<num_usr_cols; i++)
	if (*color == user_col_indx[i]) {
		*color = i+NUM_STD_COLS;
		return;
	}
    put_msg("Cannot locate user color %d, using default color for line %d.",
		*color,line_no);
    *color = DEFAULT;
    return;
}

static F_arc *
read_arcobject(fp)
FILE	*fp;
{
	F_arc	*a;
	int	n, fa, ba;
	int	type, style;
	double	thickness, wid, ht;

	if (NULL == (Arc_malloc(a))) {
	    put_msg(Err_mem);
	    return(NULL);
	    }
	a->pen = 0;
	a->fill_style = 0;
	a->for_arrow = NULL;
	a->back_arrow = NULL;
	a->next = NULL;
	if (v30_flag) {
	    n = sscanf(buf, "%*d%d%d%d%d%d%d%d%d%lf%d%d%d%d%lf%lf%d%d%d%d%d%d\n",
		&a->type, &a->style, &a->thickness, 
		&a->pen_color, &a->fill_color, &a->depth, &a->pen, &a->fill_style, 
		&a->style_val, &a->cap_style,
		&a->direction, &fa, &ba,
		&a->center.x, &a->center.y, 
		&a->point[0].x, &a->point[0].y, 
		&a->point[1].x, &a->point[1].y, 
		&a->point[2].x, &a->point[2].y);
	} else {
	    n = sscanf(buf, "%*d%d%d%d%d%d%d%d%lf%d%d%d%lf%lf%d%d%d%d%d%d\n",
		&a->type, &a->style, &a->thickness, 
		&a->pen_color, &a->depth, &a->pen, &a->fill_style, 
		&a->style_val, &a->direction, &fa, &ba,
		&a->center.x, &a->center.y, 
		&a->point[0].x, &a->point[0].y, 
		&a->point[1].x, &a->point[1].y, 
		&a->point[2].x, &a->point[2].y);
	    a->fill_color = a->pen_color;
	    a->cap_style = 0;        /* butt line cap */
	}
	if ((v30_flag && n != 21) || (!v30_flag && n != 19)) {
	    put_msg(Err_incomp, "arc", line_no);
	    free((char*)a);
	    return(NULL);
	    }
	a->thickness *= THICK_SCALE;
	a->fill_style = FILL_CONVERT(a->fill_style);
	/* keep track if pattern is used */
	note_pattern(a->fill_style);
	fix_color(&a->pen_color);
	fix_color(&a->fill_color);
	skip_comment(fp);
	if (fa) {
	    line_no++;
	    if (fscanf(fp, "%d%d%lf%lf%lf", &type, &style, &thickness, &wid, &ht) != 5) {
		fprintf(stderr, Err_incomp, "arc", line_no);
		return(NULL);
		}
	    skip_line(fp);
	    a->for_arrow = make_arrow(type, style, thickness, wid, ht);
	    skip_comment(fp);
	    }
	skip_comment(fp);
	if (ba) {
	    line_no++;
	    if (fscanf(fp, "%d%d%lf%lf%lf", &type, &style, &thickness, &wid, &ht) != 5) {
		fprintf(stderr, Err_incomp, "arc", line_no);
		return(NULL);
		}
	    skip_line(fp);
	    a->back_arrow = make_arrow(type, style, thickness, wid, ht);
	    }
	return(a);
	}

static F_compound *
read_compoundobject(fp)
FILE	*fp;
{
	F_arc		*a, *la = NULL;
	F_ellipse	*e, *le = NULL;
	F_line		*l, *ll = NULL;
	F_spline	*s, *ls = NULL;
	F_text		*t, *lt = NULL;
	F_compound	*com, *c, *lc = NULL;
	int		n, object;

	Compound_malloc(com);
	com->arcs = NULL;
	com->ellipses = NULL;
	com->lines = NULL;
	com->splines = NULL;
	com->texts = NULL;
	com->compounds = NULL;
	com->next = NULL;
	n = sscanf(buf, "%*d%d%d%d%d\n", &com->nwcorner.x, &com->nwcorner.y,
		&com->secorner.x, &com->secorner.y);
	if (n != 4) {
	    put_msg(Err_incomp, "compound", line_no);
	    free((char*)com);
	    return(NULL);
	    }
	while (get_line(fp) > 0) {
	    if (sscanf(buf, "%d", &object) != 1) {
		put_msg(Err_incomp, "compound", line_no);
		free_compound(&com);
		return(NULL);
		}
	    switch (object) {
		case O_POLYLINE :
		    if ((l = read_lineobject(fp)) == NULL) { 
			free_line(&l);
			return(NULL);
			}
		    if (ll)
			ll = (ll->next = l);
		    else 
			ll = com->lines = l;
		    break;
		case O_SPLINE :
		    if ((s = read_splineobject(fp)) == NULL) { 
			free_spline(&s);
			return(NULL);
			}
		    if (ls)
			ls = (ls->next = s);
		    else 
			ls = com->splines = s;
		    break;
		case O_ELLIPSE :
		    if ((e = read_ellipseobject()) == NULL) { 
			free_ellipse(&e);
			return(NULL);
			}
		    if (le)
			le = (le->next = e);
		    else 
			le = com->ellipses = e;
		    break;
		case O_ARC :
		    if ((a = read_arcobject(fp)) == NULL) { 
			free_arc(&a);
			return(NULL);
			}
		    if (la)
			la = (la->next = a);
		    else 
			la = com->arcs = a;
		    break;
		case O_TEXT :
		    if ((t = read_textobject(fp)) == NULL) { 
			free_text(&t);
			return(NULL);
			}
		    if (lt)
			lt = (lt->next = t);
		    else 
			lt = com->texts = t;
		    break;
		case O_COMPOUND :
		    if ((c = read_compoundobject(fp)) == NULL) { 
			free_compound(&c);
			return(NULL);
			}
		    if (lc)
			lc = (lc->next = c);
		    else 
			lc = com->compounds = c;
		    break;
		case O_END_COMPOUND :
		    return(com);
		default :
		    put_msg("Wrong object code at line %d", line_no);
		    return(NULL);
		} /*  switch */
	    }
	if (feof(fp))
	    return(com);
	else
	    return(NULL);
	}

static F_ellipse *
read_ellipseobject()
{
	F_ellipse	*e;
	int		n;

	Ellipse_malloc(e);
	e->fill_style = 0;
	e->pen = 0;
	e->next = NULL;
	if (v30_flag) {
	    n = sscanf(buf, "%*d%d%d%d%d%d%d%d%d%lf%d%lf%d%d%d%d%d%d%d%d\n",
		&e->type, &e->style, &e->thickness,
		&e->pen_color, &e->fill_color, &e->depth, &e->pen, &e->fill_style,
		&e->style_val, &e->direction, &e->angle,
		&e->center.x, &e->center.y, 
		&e->radiuses.x, &e->radiuses.y, 
		&e->start.x, &e->start.y, 
		&e->end.x, &e->end.y);
	} else {
	    n = sscanf(buf, "%*d%d%d%d%d%d%d%d%lf%d%lf%d%d%d%d%d%d%d%d\n",
		&e->type, &e->style, &e->thickness,
		&e->pen_color, &e->depth, &e->pen, &e->fill_style,
		&e->style_val, &e->direction, &e->angle,
		&e->center.x, &e->center.y, 
		&e->radiuses.x, &e->radiuses.y, 
		&e->start.x, &e->start.y, 
		&e->end.x, &e->end.y);
	    e->fill_color = e->pen_color;
	}
	if ((v30_flag && n != 19) || (!v30_flag && n != 18)) {
	    put_msg(Err_incomp, "ellipse", line_no);
	    free((char*)e);
	    return(NULL);
	    }
	fix_color(&e->pen_color);
	fix_color(&e->fill_color);
	e->thickness *= THICK_SCALE;
	e->fill_style = FILL_CONVERT(e->fill_style);
	/* keep track if pattern is used */
	note_pattern(e->fill_style);
	return(e);
	}

static F_line *
read_lineobject(fp)
FILE	*fp;
{
	F_line	*l;
	F_point	*p, *q;
	int	n, x, y, fa, ba;
	int	type, style, radius_flag;
	double	thickness, wid, ht;
	int	npts;

	Line_malloc(l);
	l->points = NULL;
	l->pen = 0;
	l->fill_style = 0;
	l->for_arrow = NULL;
	l->back_arrow = NULL;
	l->next = NULL;
	l->join_style = 0;
	l->cap_style = 0;        /* butt line cap */

	sscanf(buf,"%*d%d",&l->type);	/* get the line type */

	radius_flag = v30_flag || v21_flag || (v2_flag && l->type == T_ARC_BOX);
	if (radius_flag) {
	    if (v30_flag) {
		n = sscanf(buf, "%*d%d%d%d%d%d%d%d%d%lf%d%d%d%d%d%d",
		&l->type,&l->style,&l->thickness,&l->pen_color,&l->fill_color,
		&l->depth,&l->pen,&l->fill_style,&l->style_val,
		&l->join_style,&l->cap_style,
		&l->radius,&fa,&ba,&npts);
	    } else {
		n = sscanf(buf, "%*d%d%d%d%d%d%d%d%lf%d%d%d",
		&l->type,&l->style,&l->thickness,&l->pen_color,
		&l->depth,&l->pen,&l->fill_style,&l->style_val,&l->radius,&fa, &ba);
		l->fill_color = l->pen_color;
	    }
	}
	/* old format uses pen for radius of arc-box corners */
	else
	    {
	    n = sscanf(buf, "%*d%d%d%d%d%d%d%d%lf%d%d",
			&l->type,&l->style,&l->thickness,&l->pen_color,
			&l->depth,&l->pen,&l->fill_style,&l->style_val,&fa,&ba);
	    l->fill_color = l->pen_color;
	    if (l->type == T_ARC_BOX)
		{
		l->radius = (int) l->pen;
		l->pen = 0;
		}
	    else
		l->radius = 0;
	    }
	if ((!radius_flag && n!=10) ||
	     (radius_flag && ((!v30_flag && n!=11)||(v30_flag && n!=15)))) {
	    put_msg(Err_incomp, "line", line_no);
	    free((char*)l);
	    return(NULL);
	    }
	l->radius *= THICK_SCALE;
	l->thickness *= THICK_SCALE;
	l->fill_style = FILL_CONVERT(l->fill_style);
	/* keep track if pattern is used */
	note_pattern(l->fill_style);
	fix_color(&l->pen_color);
	fix_color(&l->fill_color);
	skip_comment(fp);
	if (fa) {
	    line_no++;
	    if (fscanf(fp, "%d%d%lf%lf%lf", &type, &style, &thickness, &wid, &ht) != 5) {
		fprintf(stderr, Err_incomp, "line", line_no);
		return(NULL);
		}
	    skip_line(fp);
	    l->for_arrow = make_arrow(type, style, thickness, wid, ht);
	    skip_comment(fp);
	    }
	if (ba) {
	    line_no++;
	    if (fscanf(fp, "%d%d%lf%lf%lf", &type, &style, &thickness, &wid, &ht) != 5) {
		fprintf(stderr, Err_incomp, "line", line_no);
		return(NULL);
		}
	    skip_line(fp);
	    l->back_arrow = make_arrow(type, style, thickness, wid, ht);
	    skip_comment(fp);
	    }
    	if (l->type == T_PIC_BOX) {
		line_no++;
		Pic_malloc(l->pic);
		if (l->pic  == NULL) {
		    free((char *) l);
		    return (NULL);
		}
		if (fscanf(fp, "%d %s", &l->pic->flipped, l->pic->file) != 2) {
	    		put_msg(Err_incomp,
				"Picture object", line_no);
	    		fprintf(stderr, Err_incomp,
				"Picture object", line_no);
	    	return (NULL);
		}
    	} else
		l->pic = NULL;

	if (NULL == (l->points = Point_malloc(p))) {
	    put_msg(Err_mem);
	    return(NULL);
	    }
	p->next = NULL;
	if (fscanf(fp, "%d%d", &p->x, &p->y) != 2) {
	    put_msg(Err_incomp, "line", line_no);
	    free_linestorage(l);
	    return(NULL);
	    }
	if (!v30_flag)
		npts = 1000000;
	for (--npts; npts > 0; npts--) {
	    if (fscanf(fp, "%d%d", &x, &y) != 2) {
		put_msg(Err_incomp, "line", line_no);
		free_linestorage(l);
		return(NULL);
		}
	    if (!v30_flag && x == 9999) 
		break;
	    if (NULL == (Point_malloc(q))) {
		put_msg(Err_mem);
		free_linestorage(l);
		return(NULL);
		}
	    q->x = x;
	    q->y = y;
	    q->next = NULL;
	    p->next = q;
	    p = q;
	    }
	skip_line(fp);
	return(l);
	}

static F_spline *
read_splineobject(fp)
FILE	*fp;
{
	F_spline	*s;
	F_point		*p, *q;
	F_control	*cp, *cq;
	int		c, n, x, y, fa, ba;
	int		type, style, npts;
	double		thickness, wid, ht;
	double		lx, ly, rx, ry;

	Spline_malloc(s);
	s->points = NULL;
	s->controls = NULL;
	s->pen = 0;
	s->fill_style = 0;
	s->for_arrow = NULL;
	s->back_arrow = NULL;
	s->next = NULL;

	if (v30_flag) {
	    n = sscanf(buf, "%*d%d%d%d%d%d%d%d%d%lf%d%d%d%d",
	    	&s->type, &s->style, &s->thickness,
		&s->pen_color, &s->fill_color,
		&s->depth, &s->pen, &s->fill_style, &s->style_val,
		&s->cap_style, &fa, &ba, &npts);
	} else {
	    n = sscanf(buf, "%*d%d%d%d%d%d%d%d%lf%d%d",
	    	&s->type, &s->style, &s->thickness, &s->pen_color,
		&s->depth, &s->pen, &s->fill_style, &s->style_val, &fa, &ba);
	    s->fill_color = s->pen_color;
	    s->cap_style = 0;        /* butt line cap */
	}
	if ((v30_flag && n != 13) || (!v30_flag && n != 10)) {
	    put_msg(Err_incomp, "spline", line_no);
	    free((char*)s);
	    return(NULL);
	    }
	s->thickness *= THICK_SCALE;
	s->fill_style = FILL_CONVERT(s->fill_style);
	/* keep track if pattern is used */
	note_pattern(s->fill_style);
	fix_color(&s->pen_color);
	fix_color(&s->fill_color);
	skip_comment(fp);
	if (fa) {
	    line_no++;
	    if (fscanf(fp, "%d%d%lf%lf%lf", &type, &style, &thickness, &wid, &ht) != 5) {
		fprintf(stderr, Err_incomp, "spline", line_no);
		return(NULL);
		}
	    skip_line(fp);
	    s->for_arrow = make_arrow(type, style, thickness, wid, ht);
	    skip_comment(fp);
	    }
	if (ba) {
	    line_no++;
	    if (fscanf(fp, "%d%d%lf%lf%lf", &type, &style, &thickness, &wid, &ht) != 5) {
		fprintf(stderr, Err_incomp, "spline", line_no);
		return(NULL);
		}
	    skip_line(fp);
	    s->back_arrow = make_arrow(type, style, thickness, wid, ht);
	    skip_comment(fp);
	    }

	/* Read points */
	if ((n = fscanf(fp, "%d%d", &x, &y)) != 2) {
	    put_msg(Err_incomp, "spline", line_no);
	    free_splinestorage(s);
	    return(NULL);
	    };
	if (NULL == (s->points = Point_malloc(p))) {
	    put_msg(Err_mem);
	    free_splinestorage(s);
	    return(NULL);
	    }
	p->x = x; p->y = y;
	c = 1;
	if (!v30_flag)
		npts = 1000000;
	for (--npts; npts > 0; npts--) {
	    if (fscanf(fp, "%d%d", &x, &y) != 2) {
		put_msg(Err_incomp, "spline", line_no);
		p->next = NULL;
		free_splinestorage(s);
		return(NULL);
		};
	    if (!v30_flag && x == 9999) 
		break;
	    if (NULL == (Point_malloc(q))) {
		put_msg(Err_mem);
		free_splinestorage(s);
		return(NULL);
		}
	    q->x = x;
	    q->y = y;
	    p->next = q;
	    p = q;
	    c++;
	    }
	p->next = NULL;
	skip_line(fp);

	if (normal_spline(s)) return(s);

	skip_comment(fp);
	/* Read controls */
	if ((n = fscanf(fp, "%lf%lf%lf%lf", &lx, &ly, &rx, &ry)) != 4) {
	    put_msg(Err_incomp, "spline", line_no);
	    free_splinestorage(s);
	    return(NULL);
	    };
	if (NULL == (s->controls = Control_malloc(cp))) {
	    put_msg(Err_mem);
	    free_splinestorage(s);
	    return(NULL);
	    }
	cp->lx = lx; cp->ly = ly;
	cp->rx = rx; cp->ry = ry;
	while (--c) {
	    if (fscanf(fp, "%lf%lf%lf%lf", &lx, &ly, &rx, &ry) != 4) {
		put_msg(Err_incomp, "spline", line_no);
		cp->next = NULL;
		free_splinestorage(s);
		return(NULL);
		};
	    if (NULL == (Control_malloc(cq))) {
		put_msg(Err_mem);
		cp->next = NULL;
		free_splinestorage(s);
		return(NULL);
		}
	    cq->lx = lx; cq->ly = ly;
	    cq->rx = rx; cq->ry = ry;
	    cp->next = cq;
	    cp = cq;
	    }
	cp->next = NULL;

	skip_line(fp);
	return(s);
	}

static F_text *
read_textobject(fp)
FILE	*fp;
{
	F_text	*t;
	int	n, ignore = 0;
	char	s[BUF_SIZE], s_temp[BUF_SIZE], junk[2];
	int	more, len, l;

	Text_malloc(t);
	t->font = 0;
	t->size = 0.0;
	t->next = NULL;

/* linux can't read 8-bit characters */
#ifdef linux
    {
	char replaced;
	int pos;
	pos = 0;
	len = strlen(buf);
	while (((unsigned char) buf[pos] <= 'e' ) &&
		((unsigned char) buf[pos] >= ' ' ) && buf[pos] )
			pos++;
	replaced = buf[pos];
	buf[pos]='f';
	if (v30_flag) {	/* order of parms is more like other objects now */
	    n = sscanf(buf, "%*d%d%d%d%d%d%lf%lf%d%lf%lf%d%d%[^f]%[f]",
		&t->type, &t->color, &t->depth, &t->pen,
		&t->font, &t->size, &t->angle,
		&t->flags, &t->height, &t->length,
		&t->base_x, &t->base_y, s, junk);
	} else {
	    n = sscanf(buf, "%*d%d%d%lf%d%d%d%lf%d%lf%lf%d%d%[^f]%[f]",
		&t->type, &t->font, &t->size, &t->pen,
		&t->color, &t->depth, &t->angle,
		&t->flags, &t->height, &t->length,
		&t->base_x, &t->base_y, s, junk);
	}
	n--;
	if ( n < 13 ) {
	    put_msg(Err_incomp, "text", line_no);
	    free((char *) t);
	    return (NULL);
	   }
	buf[pos]=replaced;
	strcpy( s, buf+pos-strlen(s));
	len=strlen(s);
	if ( len && (s[len-1] ==  '\n') )
	    s[len-1]='\0';
	len=strlen(s);
	if (!v30_flag) {		/* if older version, remove the ^A */
	    if ( len && (s[len-1] ==  1) ) {
		n++;
		s[len-1]='\0';
	    }
	}
    }

#else
	if (v30_flag) {	/* order of parms is more like other objects now,
			   string is now terminated with the literal '\001',
			   and 8-bit characters are represented as \xxx */

	    n = sscanf(buf, "%*d%d%d%d%d%d%lf%lf%d%lf%lf%d%d%[^\n]",
		&t->type, &t->color, &t->depth, &t->pen,
		&t->font, &t->size, &t->angle,
		&t->flags, &t->height, &t->length,
		&t->base_x, &t->base_y, s, junk);
	} else {
	    /* The text object is terminated by a CONTROL-A, so we read
		everything up to the CONTROL-A and then read that character.
		If we do not find the CONTROL-A on this line then this must
		be a multi-line text object and we will have to read more. */

	    n = sscanf(buf,"%*d%d%d%lf%d%d%d%lf%d%lf%lf%d%d%[^\1]%[\1]",
		&t->type, &t->font, &t->size, &t->pen,
		&t->color, &t->depth, &t->angle,
		&t->flags, &t->height, &t->length, 
		&t->base_x, &t->base_y, s, junk);
	}
	if ((n != 14) && (n != 13)) {
	  put_msg(Err_incomp, "text", line_no);
	  free((char*)t);
 	  return(NULL);
	}
#endif
	more = 0;
	if (!v30_flag && n == 13)
	    more = 1;  /* in older xfig there is more if ^A wasn't found yet */
	else if (v30_flag) {	/* in 3.0 there is more if \001 wasn't found */
	    len = strlen(s);
	    if ((strcmp(&s[len-4],"\\001") == 0) &&	/* if we find '\000' */
		(len >= 4 && s[len-5] != '\\')) {	/* and not '\\000' */
		    more = 0;				/* then there are no more lines */
		    s[len-4]='\0';			/* and get rid of the '\001' */
	    } else {
		more = 1;
		s[len++]='\n';			/* put back the end of line char */
		s[len] = '\0';			/* and terminate it */
	    }
	}
	if (more) {
	/* Read in the subsequent lines of the text if there is more than one. */
	  do {
	    fgets(buf, BUF_SIZE, fp);
	    line_no++;		/* As is done in get_line */
	    if (v30_flag) {
		if (fgets(s_temp, BUF_SIZE, fp) == NULL)
		    break;
		len = strlen(s_temp)-1;
		if ((strncmp(&s_temp[len-4],"\\001",4) == 0) &&
		    (len >= 4 && s_temp[len-5] != '\\')) {
			n=0;			/* found the '\001', set n to stop */
			s_temp[len-4]='\0';	/* and get rid of the '\001' */
		} else {
			n=1;			/* keep going (more lines) */
		}
	    } else {
		if (fgets(buf, BUF_SIZE, fp) == NULL)
		    break;
		n = sscanf(buf, "%[^\1]%[\1]", s_temp, junk);
	    }
	    /* Safety check */
	    if (strlen(s)+1 + strlen(s_temp)+1 > BUF_SIZE) {
	      /* Too many characters.  Ignore the rest. */
	      ignore = 1;
	    }
	    if (!ignore)
	      strcat(s, s_temp);
	  } while (n == 1);
	}
	if (v30_flag) {		/* now convert any \xxx to ascii characters */
	    if (strchr(s,'\\')) {
		int num;
		len = strlen(s);
		for (l=0,n=0; l < len; l++) {
		    if (s[l]=='\\') {
			if (l < len && s[l+1] != '\\') {
			    if (sscanf(&s[l+1],"%o",&num)!=1) {
				put_msg("Error in parsing text string on line",line_no);
				return(NULL);
			    }
			    buf[n++]= (unsigned char) num;	/* put char in */
			    l += 3;			/* skip over digits */
			} else {
			    buf[n++] = s[++l];		/* "\\" */
			}
		    } else {
			buf[n++] = s[l];		/* ordinary character */
		    }
		}
		buf[n]='\0';		/* terminate */
		strcpy(s,buf);		/* copy back to s */
	    }
	}
	if (strlen(s) == 0) 
		(void)strcpy(s, " ");
	t->cstring = (char*)calloc((unsigned)(strlen(s)), sizeof(char));
	if (NULL == t->cstring) {
	    put_msg(Err_mem);
	    free((char*)t);
	    return(NULL);
	}
	(void)strcpy(t->cstring, s+1);

	if (!v21_flag && (t->font == 0 || t->font == DEFAULT))
		t->flags = ((t->flags != DEFAULT) ? t->flags : 0)
				| SPECIAL_TEXT;

	if (v2_flag && !v21_flag && !special_text(t)) 
		t->flags = ((t->flags != DEFAULT) ? t->flags : 0)
				| PSFONT_TEXT;

	fix_color(&t->color);
	return(t);
}

get_line(fp)
FILE	*fp;
{
	while (1) {
	    if (NULL == fgets(buf, BUF_SIZE, fp)) {
		return(-1);
		}
	    line_no++;
	    if (*buf != '\n' && *buf != '#') 
		return(1);	/* Skip empty and comment lines */
	    }
}

skip_comment(fp)
FILE	*fp;
{
	char c;

	while ((c = fgetc(fp)) == '#') 
		skip_line(fp);
	if (c != '#') 
		ungetc(c, fp);
}

skip_line(fp)
FILE	*fp;
{
	while (fgetc(fp) != '\n') {
	    if (feof(fp)) 
		return;
	}
}

/* keep track which patterns are used (if any) */
note_pattern(fill_style)
int	 fill_style;
{
	if (fill_style >= NUMSHADES+NUMTINTS) {
	    pattern_used[fill_style-NUMSHADES-NUMTINTS] = TRUE;
	    pats_used = TRUE;
	}
}
	
init_pats_used()
{
	int i;
	pats_used = FALSE;
	for (i=0; i<NUMPATTERNS; i++)
	    pattern_used[i] = FALSE;
}
