/* Last non-groff version: main.c 1.23  (Berkeley)  85/08/05
 *
 * Adapted to GNU troff by Daniel Senderowicz 99/12/29.
 *
 * Further refinements by Werner Lemberg 00/02/20.
 *
 *
 * This file contains the main and file system dependent routines for
 * processing gremlin files into troff input.  The program watches input
 * go by to standard output, only interpreting things between .GS and
 * .GE lines.  Default values (font, size, scale, thickness) may be
 * overridden with a 'default' command and are further overridden by
 * commands in the input.
 *
 * Inside the GS and GE, commands are accepted to reconfigure the
 * picture.  At most one command may reside on each line, and each
 * command is followed by a parameter separated by white space.  The
 * commands are as follows, and may be abbreviated down to one character
 * (with exception of 'scale' and 'stipple' down to "sc" and "st") and
 * may be upper or lower case.
 *
 *                        default  -  Make all settings in the current
 *                                    .GS/.GE the global defaults.
 *                                    Height, width and file are NOT
 *                                    saved.
 *                     1, 2, 3, 4  -  Set size 1, 2, 3, or 4 (followed
 *                                    by an integer point size).
 *  roman, italics, bold, special  -  Set gremlin's fonts to any other
 *                                    troff font (1 or 2 characters).
 *                     stipple, l  -  Use a stipple font for polygons.
 *                                    Arg is troff font name.  No
 *                                    default.  Can use only one stipple
 *                                    font per picture.  (See below for
 *                                    stipple font index.)
 *                       scale, x  -  Scale is IN ADDITION to the global
 *                                    scale factor from the default.
 *                     pointscale  -  Turn on scaling point sizes to
 *                                    match 'scale' commands.  (Optional
 *                                    operand 'off' to turn it off.)
 *          narrow, medium, thick  -  Set widths of lines.
 *                           file  -  Set the file name to read the
 *                                    gremlin picture from.  If the file
 *                                    isn't in the current directory,
 *                                    the gremlin library is tried.
 *                  width, height  -  These two commands override any
 *                                    scaling factor that is in effect,
 *                                    and forces the picture to fit into
 *                                    either the height or width
 *                                    specified, whichever makes the
 *                                    picture smaller.  The operand for
 *                                    these two commands is a
 *                                    floating-point number in units of
 *                                    inches.
 *            l<nn> (integer <nn>) -  Set association between stipple
 *                                    <nn> and a stipple 'character'.
 *                                    <nn> must be in the range 0 to
 *                                    NSTIPPLES (16) inclusive.  The
 *                                    integer operand is an index in the
 *                                    stipple font selected.  Valid cf
 *                                    (cifplot) indices are 1-32
 *                                    (although 24 is not defined),
 *                                    valid ug (unigrafix) indices are
 *                                    1-14, and valid gs (gray scale)
 *                                    indices are 0-16.  Nonetheless,
 *                                    any number between 0 and 255 is
 *                                    accepted since new stipple fonts
 *                                    may be added.  An integer operand
 *                                    is required.
 *
 * Troff number registers used: g1 through g9.  g1 is the width of the
 * picture, and g2 is the height.  g3, and g4, save information, g8 and
 * g9 are used for text processing and g5-g7 are reserved.
 */


#include "lib.h"

#include <ctype.h>
#include <stdlib.h>
#include <errno.h> // errno
#include "gprint.h"

#include "device.h"
#include "font.h"
#include "searchpath.h"
#include "macropath.h"

#include "errarg.h"
#include "error.h"
#include "defs.h"

extern "C" const char *Version_string;

/* database imports */

extern void HGPrintElt(ELT *element, int baseline);
extern ELT *DBInit();
extern ELT *DBRead(FILE *file);
extern POINT *PTInit();
extern POINT *PTMakePoint(double x, double y, POINT **pplist);

#define INIT_FILE_SIZE 50  /* Initial sz of file array from cmd line. */
#define FILE_SIZE_INCR 50  /* Amount to increase array of files by. */

#define SUN_SCALEFACTOR 0.70

/* #define DEFSTIPPLE    "gs" */
#define DEFSTIPPLE	"cf"
/*
 * This grn implementation emits '.st' requests to control stipple
 * effects, but groff does not (currently) support any such request.
 *
 * This hack disables the emission of such requests, without destroying
 * the infrastructure necessary to support the feature in the future; to
 * enable the emission of '.st' requests, at a future date when groff
 * can support them, simply rewrite the following #define as:
 *
 *   #define USE_ST_REQUEST  stipple
 *
 * with accompanying comment: "emit '.st' requests as required".
 */
#define USE_ST_REQUEST  0	/* never emit '.st' requests */

#define MAXINLINE	100	/* input line length */

#define SCREENtoINCH	0.02	/* scaling factor, screen to inches */

#define BIG	999999999999.0	/* unwieldy large floating number */


/* static char sccsid[] = "@(#) (Berkeley) 8/5/85, 12/28/99"; */

int res;			/* the printer's resolution goes here */

int dotshifter;			/* for the length of dotted curves */

double linethickness;		/* brush styles */
int linmod;
int lastx;			/* point registers for printing */
int lasty;			/* elements                     */
int lastyline;			/* A line's vertical position is NOT  */
				/* the same after that line is over,  */
				/* so for a line of drawing commands, */
				/* vertical spacing is kept in        */
				/* lastyline.                         */

/* These are the default fonts, sizes, line styles, */
/* and thicknesses.  They can be modified from a    */
/* 'default' command and are reset each time the    */
/* start of a picture (.GS) is found.               */

const char *deffont[] =
{"R", "I", "B", "S"};
int defsize[] =
{10, 16, 24, 36};
/* #define BASE_THICKNESS 1.0 */
#define BASE_THICKNESS 0.15
double defthick[STYLES] =
{1 * BASE_THICKNESS,
 1 * BASE_THICKNESS,
 5 * BASE_THICKNESS,
 1 * BASE_THICKNESS,
 1 * BASE_THICKNESS,
 3 * BASE_THICKNESS};

/* int cf_stipple_index[NSTIPPLES + 1] =                              */
/* {0, 1, 3, 12, 14, 16, 19, 21, 23};                                 */
/* a logarithmic scale looks better than a linear one for gray shades */
/*                                                                    */
/* int other_stipple_index[NSTIPPLES + 1] =                           */
/* {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};        */

int cf_stipple_index[NSTIPPLES + 1] =
{0, 18, 32, 56, 100, 178, 316, 562, 1000};	/* only 1-8 used */
int other_stipple_index[NSTIPPLES + 1] =
{0, 62, 125, 187, 250, 312, 375, 437, 500,
 562, 625, 687, 750, 812, 875, 937, 1000};

/* int *defstipple_index = other_stipple_index; */
int *defstipple_index = cf_stipple_index;

int style[STYLES] =
{DOTTED, DOTDASHED, SOLID, DASHED, SOLID, SOLID};
double scale = 1.0;		/* no scaling, default */
int defpoint = 0;		/* flag for point size scaling */
char *defstipple = (char *) 0;
enum E {
  OUTLINE, FILL, BOTH
} polyfill;

/* flag to control filling of polygons */

double adj1 = 0.0;
double adj2 = 0.0;
double adj3 = 0.0;
double adj4 = 0.0;

double thick[STYLES];		/* thicknesses set by defaults, then  */
				/* by commands                        */
char *tfont[FONTS];		/* fonts originally set to deffont    */
				/* values, then optionally changed by */
int tsize[SIZES];		/* commands inside grn                */
int stipple_index[NSTIPPLES + 1];	/* stipple font file indices  */
char *stipple;

double xscale;		/* scaling factor from individual pictures */
double troffscale;	/* scaling factor at output time */

double width;		/* user-request maximum width for picture */
			/* (in inches)                            */
double height;		/* user-request height */
int pointscale;		/* flag for point size scaling */
int setdefault;		/* flag for a .GS/.GE to remember all */
			/* settings                           */
int sflag;		/* -s flag: sort order (do polyfill first) */

double toppoint;	/* remember the picture */
double bottompoint;	/* bounds in these variables */
double leftpoint;
double rightpoint;

int ytop;		/* these are integer versions of the above */
int ybottom;		/* so not to convert each time they're used */
int xleft;
int xright;

static int linenum = 0;		/* line number of troff input file */
char inputline[MAXINLINE];	/* spot to filter through the file */
char *c1 = inputline;		/* c1, c2, and c3 will be used to */
char *c2 = inputline + 1;	/* hunt for lines that begin with */
char *c3 = inputline + 2;	/* '.GS' by looking individually */
char *c4 = inputline + 3;	/* needed for compatibility mode */
char GScommand[MAXINLINE];	/* put user's '.GS' command line here */
char gremlinfile[MAXINLINE];	/* filename to use for a picture */
int SUNFILE = FALSE;		/* TRUE if SUN gremlin file */
int compatibility_flag = FALSE;	/* TRUE if in compatibility mode */


void getres();
int doinput(FILE *fp);
void conv(FILE *fp, int baseline);
void savestate();
int has_polygon(ELT *elist);
void interpret(char *line);

void *
grnmalloc(size_t size,
	  const char *what)
{
  void *ptr = 0;
  ptr = malloc(size);
  if (!ptr) {
    fatal("memory allocation failed for %1: %2", what, strerror(errno));
  }
  return ptr;
}

void
usage(FILE *stream)
{
  fprintf(stream,
	  "usage: %s [-Cs] [-M dir] [-F dir] [-T dev] [file ...]\n"
	  "usage: %s {-v | --version}\n"
	  "usage: %s --help\n",
	  program_name, program_name, program_name);
}


/* Add a new file entry in the array, expanding array if needs be. */

char **
add_file(char **file,
	 char *new_file,
	 int *count,
	 int *cur_size)
{
  if (*count >= *cur_size) {
    *cur_size += FILE_SIZE_INCR;
    file = (char **) realloc(file, *cur_size * sizeof(char *));
    if (file == NULL) {
      fatal("unable to extend file array");
    }
  }
  file[*count] = new_file;
  *count += 1;

  return file;
}


/*--------------------------------------------------------------------*
 | Routine:	main (argument_count, argument_pointer)
 |
 | Results:	Parses the command line, accumulating input file names,
 |		then reads the inputs, passing it directly to output
 |		until a '.GS' line is read.  Main then passes control to
 |		'conv' to do the gremlin file conversions.
 *--------------------------------------------------------------------*/

int
main(int argc,
     char **argv)
{
  setlocale(LC_NUMERIC, "C");
  program_name = argv[0];
  FILE *fp;
  int k;
  char c;
  int gfil = 0;
  char **file = NULL;
  int file_cur_size = INIT_FILE_SIZE;
  char *operand(int *argcp, char ***argvp);

  file = (char **) grnmalloc(file_cur_size * sizeof(char *),
			     "file array");
  while (--argc) {
    if (**++argv != '-')
      file = add_file(file, *argv, &gfil, &file_cur_size);
    else
      switch (c = (*argv)[1]) {

      case 0:
	file = add_file(file, NULL, &gfil, &file_cur_size);
	break;

      case 'C':		/* compatibility mode */
	compatibility_flag = TRUE;
	break;

      case 'F':		/* font path to find DESC */
	font::command_line_font_dir(operand(&argc, &argv));
	break;

      case 'T':		/* final output typesetter name */
	device = operand(&argc, &argv);
	break;

      case 'M':		/* set library directory */
	macro_path.command_line_dir(operand(&argc, &argv));
	break;

      case 's':		/* preserve order of elements */
	sflag = 1;
	break;

      case '-':
	if (strcmp(*argv,"--version")==0) {
      case 'v':
	  printf("GNU grn (groff) version %s\n", Version_string);
	  exit(0);
	  break;
	}
	if (strcmp(*argv,"--help")==0) {
      case '?':
	  usage(stdout);
	  exit(0);
	  break;
	}
	// fallthrough
      default:
	error("unknown switch: %1", c);
	usage(stderr);
	exit(1);
      }
  }

  getres();		/* set the resolution for an output device */

  if (gfil == 0) {		/* no filename, use standard input */
    file[0] = NULL;
    gfil++;
  }

  for (k = 0; k < gfil; k++) {
    if (file[k] != NULL) {
      if ((fp = fopen(file[k], "r")) == NULL)
	fatal("can't open %1", file[k]);
    } else
      fp = stdin;

    while (doinput(fp)) {
      if (*c1 == '.' && *c2 == 'G' && *c3 == 'S') {
	if (compatibility_flag ||
	    *c4 == '\n' || *c4 == ' ' || *c4 == '\0')
	  conv(fp, linenum);
	else
	  fputs(inputline, stdout);
      } else
	fputs(inputline, stdout);
    }
  }

  return 0;
}


/*--------------------------------------------------------------------*
 | Routine:	char  * operand (& argc, & argv)
 |
 | Results:	Returns address of the operand given with a command-line
 |		option.  It uses either '-Xoperand' or '-X operand',
 |		whichever is present.  The program is terminated if no
 |		option is present.
 |
 | Side Efct:	argc and argv are updated as necessary.
 *--------------------------------------------------------------------*/

char *
operand(int *argcp,
	char ***argvp)
{
  if ((**argvp)[2])
    return (**argvp + 2);	/* operand immediately follows */
  if ((--*argcp) <= 0) {	/* no operand */
    error("command-line option operand missing.");
    exit(8);
  }
  return (*(++(*argvp)));	/* operand is next word */
}


/*--------------------------------------------------------------------*
 | Routine:	getres ()
 |
 | Results:	Sets 'res' to the resolution of the output device.
 *--------------------------------------------------------------------*/

void
getres()
{
  int linepiece;

  if (0 /* nullptr */ == font::load_desc())
    fatal("cannot load 'DESC' description file for device '%1'",
	  device);

  res = font::res;

  /* Correct the brush thicknesses based on res */
  /* if (res >= 256) {
      defthick[0] = res >> 8;
      defthick[1] = res >> 8;
      defthick[2] = res >> 4;
      defthick[3] = res >> 8;
      defthick[4] = res >> 8;
      defthick[5] = res >> 6;
      } */

  linepiece = res >> 9;
  for (dotshifter = 0; linepiece; dotshifter++)
    linepiece = linepiece >> 1;
}


/*--------------------------------------------------------------------*
 | Routine:	int  doinput (file_pointer)
 |
 | Results:	A line of input is read into 'inputline'.
 |
 | Side Efct:	"linenum" is incremented.
 |
 | Bugs:	Lines longer than MAXINLINE are NOT checked, except for
 |		updating 'linenum'.
 *--------------------------------------------------------------------*/

int
doinput(FILE *fp)
{
  if (fgets(inputline, MAXINLINE, fp) == NULL)
    return 0;
  if (strchr(inputline, '\n'))	/* ++ only if it's a complete line */
    linenum++;
  return 1;
}


/*--------------------------------------------------------------------*
 | Routine:	initpic ( )
 |
 | Results:	Sets all parameters to the normal defaults, possibly
 |		overridden by a setdefault command.  Initialize the
 |		picture variables, and output the startup commands to
 |		troff to begin the picture.
 *--------------------------------------------------------------------*/

void
initpic()
{
  int i;

  for (i = 0; i < STYLES; i++) {	/* line thickness defaults */
    thick[i] = defthick[i];
  }
  for (i = 0; i < FONTS; i++) {		/* font name defaults */
    tfont[i] = (char *)deffont[i];
  }
  for (i = 0; i < SIZES; i++) {		/* font size defaults */
    tsize[i] = defsize[i];
  }
  for (i = 0; i <= NSTIPPLES; i++) {	/* stipple font file default */
					/* indices                   */
    stipple_index[i] = defstipple_index[i];
  }
  stipple = defstipple;

  gremlinfile[0] = 0;		/* filename is 'null' */
  setdefault = 0;		/* not the default settings (yet) */

  toppoint = BIG;		/* set the picture bounds out */
  bottompoint = -BIG;		/* of range so they'll be set */
  leftpoint = BIG;		/* by 'savebounds' on input */
  rightpoint = -BIG;

  pointscale = defpoint;/* flag for scaling point sizes default */
  xscale = scale;	/* default scale of individual pictures */
  width = 0.0;		/* size specifications input by user */
  height = 0.0;

  linethickness = DEFTHICK;	/* brush styles */
  linmod = DEFSTYLE;
}


/*--------------------------------------------------------------------*
 | Routine:	conv (file_pointer, starting_line)
 |
 | Results:	At this point, we just passed a '.GS' line in the input
 |		file.  conv reads the input and calls 'interpret' to
 |		process commands, gathering up information until a '.GE'
 |		line is found.  It then calls 'HGPrint' to do the
 |		translation of the gremlin file to troff commands.
 *--------------------------------------------------------------------*/

void
conv(FILE *fp,
     int baseline)
{
  FILE *gfp = NULL;	/* input file pointer */
  int done = 0;	/* flag to remember if finished */
  ELT *e;		/* current element pointer */
  ELT *PICTURE;		/* whole picture data base pointer */
  double temp;		/* temporary calculating area */
  /* POINT ptr; */	/* coordinates of a point to pass to 'mov' */
			/* routine                                 */
  int flyback;		/* flag 'want to end up at the top of the */
			/* picture?'                              */
  int compat;		/* test character after .GE or .GF */


  initpic();			/* set defaults, ranges, etc. */
  strcpy(GScommand, inputline);	/* save '.GS' line for later */

  do {
    done = !doinput(fp);		/* test for EOF */
    flyback = (*c3 == 'F');		/* and .GE or .GF */
    compat = (compatibility_flag ||
	      *c4 == '\n' || *c4 == ' ' || *c4 == '\0');
    done |= (*c1 == '.' && *c2 == 'G' && (*c3 == 'E' || flyback) &&
	     compat);

    if (done) {
      if (setdefault)
	savestate();

      if (!gremlinfile[0]) {
	if (!setdefault)
	  error("no picture file name at line %1", baseline);
	return;
      }
      char *path;
      gfp = macro_path.open_file(gremlinfile, &path);
      if (0 /* nullptr */ == gfp) {
	error("cannot open picture file '%1'", gremlinfile);
	return;
      }
      PICTURE = DBRead(gfp);	/* read picture file */
      fclose(gfp);
      free(path);
      if (DBNullelt(PICTURE))
	return;			/* If a request is made to make the  */
				/* picture fit into a specific area, */
				/* set the scale to do that.         */

      if (stipple == (char *) NULL)	/* if user forgot stipple    */
	if (has_polygon(PICTURE))	/* and picture has a polygon */
	  stipple = (char *)DEFSTIPPLE;	/* then set the default      */

      if ((temp = bottompoint - toppoint) < 0.1)
	temp = 0.1;
      temp = (height != 0.0) ? height / (temp * SCREENtoINCH) : BIG;
      if ((troffscale = rightpoint - leftpoint) < 0.1)
	troffscale = 0.1;
      troffscale = (width != 0.0) ?
	  width / (troffscale * SCREENtoINCH) : BIG;
      if (temp == BIG && troffscale == BIG)
	troffscale = xscale;
      else {
	if (temp < troffscale)
	  troffscale = temp;
      }				/* here, troffscale is the  */
				/* picture's scaling factor */
      if (pointscale) {
	int i;			/* do point scaling here, when   */
				/* scale is known, before output */
	for (i = 0; i < SIZES; i++)
	  tsize[i] = (int) (troffscale * (double) tsize[i] + 0.5);
      }

					/* change to device units */
      troffscale *= SCREENtoINCH * res;	/* from screen units      */

      /* Calculate integer versions of the picture limits. */
      ytop = (int) (toppoint * troffscale);
      ybottom = (int) (bottompoint * troffscale);
      xleft = (int) (leftpoint * troffscale);
      xright = (int) (rightpoint * troffscale);

      /* save stuff in number registers,    */
      /*   register g1 = picture width and  */
      /*   register g2 = picture height,    */
      /*   set vertical spacing, no fill,   */
      /*   and break (to make sure picture  */
      /*   starts on left), and put out the */
      /*   user's '.GS' line.               */
      printf(".br\n"
	     ".nr g1 %du\n"
	     ".nr g2 %du\n"
	     "%s"
	     ".nr g3 \\n(.f\n"
	     ".nr g4 \\n(.s\n"
	     "\\0\n"
	     ".sp -1\n",
	     xright - xleft, ybottom - ytop, GScommand);

      if (USE_ST_REQUEST)	/* stipple requested for this picture */
	printf(".st %s\n", stipple);
      lastx = xleft;		/* note where we are (upper left */
      lastyline = lasty = ytop;	/* corner of the picture)        */

      /* Just dump everything in the order it appears.
       *
       * If -s command-line option, traverse picture twice: First time,
       * print only the interiors of filled polygons (as borderless
       * polygons).  Second time, print the outline as series of line
       * segments.  This way, postprocessors that overwrite rather than
       * merge picture elements (such as Postscript) can still have text
       * and graphics on a shaded background.
       */
      /* if (sflag) */
      if (!sflag) {	/* changing the default for filled polygons */
	e = PICTURE;
	polyfill = FILL;
	while (!DBNullelt(e)) {
	  printf(".mk\n");
	  if (e->type == POLYGON)
	    HGPrintElt(e, baseline);
	  printf(".rt\n");
	  lastx = xleft;
	  lastyline = lasty = ytop;
	  e = DBNextElt(e);
	}
      }
      e = PICTURE;

      /* polyfill = !sflag ? BOTH : OUTLINE; */
      polyfill = sflag ? BOTH : OUTLINE;	/* changing default */
      while (!DBNullelt(e)) {
	printf(".mk\n");
	HGPrintElt(e, baseline);
	printf(".rt\n");
	lastx = xleft;
	lastyline = lasty = ytop;
	e = DBNextElt(e);
      }

      /* decide where to end picture */

      /* I [Senderowicz?] changed everything here.  I always use the */
      /* combination .mk and .rt, so once finished I just space down */
      /* height of the picture that is \n(g2u.                       */
      if (flyback) {		/* end picture at upper left */
	/* ptr.x = leftpoint;
	   ptr.y = toppoint; */
      } else {			/* end picture at lower left */
	/* ptr.x = leftpoint;
	   ptr.y = bottompoint; */
	printf(".sp \\n(g2u\n");
      }

      /* tmove(&ptr); */	/* restore default line parameters */

      /* Restore everything to the way it was before the .GS, then */
      /* put out the '.GE' line from user                          */

      /* printf("\\D't %du'\\D's %du'\n", DEFTHICK, DEFSTYLE); */
      /* groff doesn't understand the \Ds command */

      printf("\\D't %du'\n", DEFTHICK);
      if (flyback)		/* make sure we end up at top of */
	printf(".sp -1\n");	/* picture if 'flying back'      */
      if (USE_ST_REQUEST)	/* restore stipple to previous */
	printf(".st\n");
      printf(".br\n"
	     ".ft \\n(g3\n"
	     ".ps \\n(g4\n"
	     "%s", inputline);
    } else
      interpret(inputline);	/* take commands from the input file */
  } while (!done);
}


/*--------------------------------------------------------------------*
 | Routine:	savestate  ( )
 |
 | Results:	All the current scaling/font size/font name/thickness/
 |		pointscale settings are made the defaults.  Scaled
 |		point sizes are NOT saved.  The scaling is done each
 |		time a new picture is started.
 |
 | Side Efct:	scale, and def* are modified.
 *--------------------------------------------------------------------*/

void
savestate()
{
  int i;

  for (i = 0; i < STYLES; i++)	/* line thickness defaults */
    defthick[i] = thick[i];
  for (i = 0; i < FONTS; i++)	/* font name defaults */
    deffont[i] = tfont[i];
  for (i = 0; i < SIZES; i++)	/* font size defaults */
    defsize[i] = tsize[i];
  /* stipple font file default indices */
  for (i = 0; i <= NSTIPPLES; i++)
    defstipple_index[i] = stipple_index[i];

  defstipple = stipple;	/* if stipple has been set, it's remembered */
  scale *= xscale;	/* default scale of individual pictures */
  defpoint = pointscale;/* flag to scale point sizes from x factors */
}


/*--------------------------------------------------------------------*
 | Routine:	savebounds (x_coordinate, y_coordinate)
 |
 | Results:	Keeps track of the maximum and minimum extent of a
 |		picture in the global variables: left-, right-, top- and
 |		bottompoint.  'savebounds' assumes that the points have
 |		been oriented to the correct direction.  No scaling has
 |		taken place, though.
 *--------------------------------------------------------------------*/

void
savebounds(double x,
	   double y)
{
  if (x < leftpoint)
    leftpoint = x;
  if (x > rightpoint)
    rightpoint = x;
  if (y < toppoint)
    toppoint = y;
  if (y > bottompoint)
    bottompoint = y;
}


/*--------------------------------------------------------------------*
 | Routine:	interpret (character_string)
 |
 | Results:	Commands are taken from the input string and performed.
 |		Commands are separated by newlines, and are of the
 |		format:
 |			string1 string2
 |		where string1 is the command, string2 the argument.
 |
 | Side Efct:	Font and size strings, plus the gremlin file name and
 |		the width and height variables are set by this routine.
 *--------------------------------------------------------------------*/

void
interpret(char *line)
{
  char str1[MAXINLINE];
  char str2[MAXINLINE];
  char *chr;
  int i;
  double par;

  str2[0] = '\0';
  sscanf(line, "%80s%80s", &str1[0], &str2[0]);
  for (chr = &str1[0]; *chr; chr++)	/* convert command to */
    if (isupper(*chr))
      *chr = tolower(*chr);	/* lower case */

  switch (str1[0]) {

  case '1':
  case '2':			/* font sizes */
  case '3':
  case '4':
    i = atoi(str2);
    if (i > 0 && i < 1000)
      tsize[str1[0] - '1'] = i;
    else
      error("bad font size value at line %1", linenum);
    break;

  case 'r':			/* roman */
    if (str2[0] < '0')
      goto nofont;
    tfont[0] = (char *) grnmalloc(strlen(str2) + 1, "roman command");
    strcpy(tfont[0], str2);
    break;

  case 'i':			/* italics */
    if (str2[0] < '0')
      goto nofont;
    tfont[1] = (char *) grnmalloc(strlen(str2) + 1, "italics command");
    strcpy(tfont[1], str2);
    break;

  case 'b':			/* bold */
    if (str2[0] < '0')
      goto nofont;
    tfont[2] = (char *) grnmalloc(strlen(str2) + 1, "bold command");
    strcpy(tfont[2], str2);
    break;

  case 's':			/* special */
    if (str1[1] == 'c')
      goto scalecommand;	/* or scale */

    if (str2[0] < '0') {
  nofont:
      error("no font name specified in line %1", linenum);
      break;
    }
    if (str1[1] == 't')
      goto stipplecommand;	/* or stipple */

    tfont[3] = (char *) grnmalloc(strlen(str2) + 1, "special command");
    strcpy(tfont[3], str2);
    break;

  case 'l':			/* l */
    if (isdigit(str1[1])) {	/* set stipple index */
      int idx = atoi(str1 + 1), val;

      if (idx < 0 || idx > NSTIPPLES) {
	error("bad stipple number %1 at line %2", idx, linenum);
	break;
      }
      if (!defstipple_index)
	defstipple_index = other_stipple_index;
      val = atoi(str2);
      if (val >= 0 && val < 256)
	stipple_index[idx] = val;
      else
	error("bad stipple index value at line %1", linenum);
      break;
    }

  stipplecommand:		/* set stipple name */
    stipple = (char *) grnmalloc(strlen(str2) + 1, "stipple command");
    strcpy(stipple, str2);
    /* if it's a 'known' font (currently only 'cf'), set indices    */
    if (strcmp(stipple, "cf") == 0)
      defstipple_index = cf_stipple_index;
    else
      defstipple_index = other_stipple_index;
    for (i = 0; i <= NSTIPPLES; i++)
      stipple_index[i] = defstipple_index[i];
    break;

  case 'a':			/* text adjust */
    par = atof(str2);
    switch (str1[1]) {
    case '1':
      adj1 = par;
      break;
    case '2':
      adj2 = par;
      break;
    case '3':
      adj3 = par;
      break;
    case '4':
      adj4 = par;
      break;
    default:
      error("bad adjust command at line %1", linenum);
      break;
    }
    break;

  case 't':			/* thick */
    thick[2] = defthick[0] * atof(str2);
    break;

  case 'm':			/* medium */
    thick[5] = defthick[0] * atof(str2);
    break;

  case 'n':			/* narrow */
    thick[0] = thick[1] = thick[3] = thick[4] =
	defthick[0] * atof(str2);
    break;

  case 'x':			/* x */
  scalecommand:			/* scale */
    par = atof(str2);
    if (par > 0.0)
      xscale *= par;
    else
      error("invalid scale value on line %1", linenum);
    break;

  case 'f':			/* file */
    strcpy(gremlinfile, str2);
    break;

  case 'w':			/* width */
    width = atof(str2);
    if (width < 0.0)
      width = -width;
    break;

  case 'h':			/* height */
    height = atof(str2);
    if (height < 0.0)
      height = -height;
    break;

  case 'd':			/* defaults */
    setdefault = 1;
    break;

  case 'p':			/* pointscale */
    if (strcmp("off", str2))
      pointscale = 1;
    else
      pointscale = 0;
    break;

  default:
    error("unknown command '%1' on line %2", str1, linenum);
    exit(8);
    break;
  };
}


/*
 * return TRUE if picture contains a polygon
 * otherwise FALSE
 */

int
has_polygon(ELT *elist)
{
  while (!DBNullelt(elist)) {
    if (elist->type == POLYGON)
      return (1);
    elist = DBNextElt(elist);
  }

  return (0);
}

// Local Variables:
// fill-column: 72
// mode: C++
// End:
// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
