/* Last non-groff version: hdb.c  1.8 (Berkeley) 84/10/20
 *
 * Copyright -C- 1982 Barry S. Roitblat
 *
 * This file contains database routines for the hard copy programs of the
 * gremlin picture editor.
 */

#include "gprint.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

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

#define MAXSTRING 128

/* imports from main.cc */

extern int linenum;		/* current line number in input file */
extern char gremlinfile[];	/* name of file currently reading */
extern int SUNFILE;		/* TRUE if SUN gremlin file */
extern void savebounds(float x, float y);

/* imports from hpoint.cc */

extern POINT *PTInit();
extern POINT *PTMakePoint(float x, float y, POINT ** pplist);


int DBGetType(register char *s);


/*
 * This routine returns a pointer to an initialized database element which
 * would be the only element in an empty list.
 */
ELT *
DBInit()
{
  return ((ELT *) NULL);
}				/* end DBInit */


/*
 * This routine creates a new element with the specified attributes and
 * links it into database.
 */
ELT *
DBCreateElt(int type,
	    POINT * pointlist,
	    int brush,
	    int size,
	    char *text,
	    ELT **db)
{
  register ELT *temp;

  temp = (ELT *) malloc(sizeof(ELT));
  temp->nextelt = *db;
  temp->type = type;
  temp->ptlist = pointlist;
  temp->brushf = brush;
  temp->size = size;
  temp->textpt = text;
  *db = temp;
  return (temp);
}				/* end CreateElt */


/*
 * This routine reads the specified file into a database and returns a
 * pointer to that database.
 */
ELT *
DBRead(register FILE *file)
{
  register int i;
  register int done;		/* flag for input exhausted */
  register float nx;		/* x holder so x is not set before orienting */
  int type;			/* element type */
  ELT *elist;			/* pointer to the file's elements */
  POINT *plist;			/* pointer for reading in points */
  char string[MAXSTRING], *txt;
  float x, y;			/* x and y are read in point coords */
  int len, brush, size;
  int lastpoint;

  SUNFILE = FALSE;
  elist = DBInit();
  (void) fscanf(file, "%s\n", string);
  if (strcmp(string, "gremlinfile")) {
    if (strcmp(string, "sungremlinfile")) {
      error("`%1' is not a gremlin file", gremlinfile);
      return (elist);
    }
    SUNFILE = TRUE;
  }

  (void) fscanf(file, "%d%f%f\n", &size, &x, &y);
  /* ignore orientation and file positioning point */

  done = FALSE;
  while (!done) {
    /* if (fscanf(file,"%s\n", string) == EOF) */
    /* I changed the scanf format because the element */
    /* can have two words (e.g. CURVE SPLINE)         */
    if (fscanf(file, "\n%[^\n]\n", string) == EOF) {
      error("`%1', error in file format", gremlinfile);
      return (elist);
    }

    type = DBGetType(string);	/* interpret element type */
    if (type < 0) {		/* no more data */
      done = TRUE;
      (void) fclose(file);
    } else {
#ifdef UW_FASTSCAN
      (void) xscanf(file, &x, &y);		/* always one point */
#else
      (void) fscanf(file, "%f%f\n", &x, &y);	/* always one point */
#endif	/* UW_FASTSCAN */
      plist = PTInit();		/* NULL point list */

      /*
       * Files created on the SUN have point lists terminated by a line
       * containing only an asterik ('*').  Files created on the AED have
       * point lists terminated by the coordinate pair (-1.00 -1.00).
       */
      if (TEXT(type)) {		/* read only first point for TEXT elements */
	nx = xorn(x, y);
	y = yorn(x, y);
	(void) PTMakePoint(nx, y, &plist);
	savebounds(nx, y);

#ifdef UW_FASTSCAN
	while (xscanf(file, &x, &y));
#else
	lastpoint = FALSE;
	do {
	  fgets(string, MAXSTRING, file);
	  if (string[0] == '*') {	/* SUN gremlin file */
	    lastpoint = TRUE;
	  } else {
	    (void) sscanf(string, "%f%f", &x, &y);
	    if ((x == -1.00 && y == -1.00) && (!SUNFILE))
	      lastpoint = TRUE;
	  }
	} while (!lastpoint);
#endif	/* UW_FASTSCAN */
      } else {			/* not TEXT element */
#ifdef UW_FASTSCAN
	do {
	  nx = xorn(x, y);
	  y = yorn(x, y);
	  (void) PTMakePoint(nx, y, &plist);
	  savebounds(nx, y);
	} while (xscanf(file, &x, &y));
#else
	lastpoint = FALSE;
	while (!lastpoint) {
	  nx = xorn(x, y);
	  y = yorn(x, y);
	  (void) PTMakePoint(nx, y, &plist);
	  savebounds(nx, y);

	  fgets(string, MAXSTRING, file);
	  if (string[0] == '*') {	/* SUN gremlin file */
	    lastpoint = TRUE;
	  } else {
	    (void) sscanf(string, "%f%f", &x, &y);
	    if ((x == -1.00 && y == -1.00) && (!SUNFILE))
	      lastpoint = TRUE;
	  }
	}
#endif	/* UW_FASTSCAN */
      }
      (void) fscanf(file, "%d%d\n", &brush, &size);
      (void) fscanf(file, "%d", &len);	/* text length */
      (void) getc(file);		/* eat blank */
      txt = (char *) malloc((unsigned) len + 1);
      for (i = 0; i < len; ++i) {	/* read text */
	txt[i] = getc(file);
      }
      txt[len] = '\0';
      (void) DBCreateElt(type, plist, brush, size, txt, &elist);
    }				/* end else */
  } /* end while not done */ ;
  return (elist);
}				/* end DBRead */


/*
 * Interpret element type in string s.
 * Old file format consisted of integer element types.
 * New file format has literal names for element types.
 */
int
DBGetType(register char *s)
{
  if (isdigit(s[0]) || (s[0] == '-'))	/* old element format or EOF */
    return (atoi(s));

  switch (s[0]) {
  case 'P':
    return (POLYGON);
  case 'V':
    return (VECTOR);
  case 'A':
    return (ARC);
  case 'C':
    if (s[1] == 'U')
      return (CURVE);
    switch (s[4]) {
    case 'L':
      return (CENTLEFT);
    case 'C':
      return (CENTCENT);
    case 'R':
      return (CENTRIGHT);
    default:
      fatal("unknown element type");
    }
  case 'B':
    switch (s[3]) {
    case 'L':
      return (BOTLEFT);
    case 'C':
      return (BOTCENT);
    case 'R':
      return (BOTRIGHT);
    default:
      fatal("unknown element type");
    }
  case 'T':
    switch (s[3]) {
    case 'L':
      return (TOPLEFT);
    case 'C':
      return (TOPCENT);
    case 'R':
      return (TOPRIGHT);
    default:
      fatal("unknown element type");
    }
  default:
    fatal("unknown element type");
  }

  return 0;				/* never reached */
}

#ifdef UW_FASTSCAN
/*
 * Optimization hack added by solomon@crys.wisc.edu, 12/2/86.
 * A huge fraction of the time was spent reading floating point numbers from
 * the input file, but the numbers always have the format 'ddd.dd'.  Thus
 * the following special-purpose version of fscanf.
 *
 * xscanf(f,xp,yp) does roughly what fscanf(f,"%f%f",xp,yp) does except:
 *   -the next piece of input must be of the form
 *      <space>* <digit>*'.'<digit>* <space>* <digit>*'.'<digit>*
 *   -xscanf eats the character following the second number
 *   -xscanf returns 0 for "end-of-data" indication, 1 otherwise, where
 *    end-of-data is signalled by a '*' [in which case the rest of the
 *    line is gobbled], or by '-1.00 -1.00' [but only if !SUNFILE].
 */
int
xscanf(FILE *f,
       float *xp,
       float *yp)
{
  register int c, i, j, m, frac;
  int iscale = 1, jscale = 1;	/* x = i/scale, y=j/jscale */

  while ((c = getc(f)) == ' ');
  if (c == '*') {
    while ((c = getc(f)) != '\n');
    return 0;
  }
  i = m = frac = 0;
  while (isdigit(c) || c == '.' || c == '-') {
    if (c == '-') {
      m++;
      c = getc(f);
      continue;
    }
    if (c == '.')
      frac = 1;
    else {
      if (frac)
	iscale *= 10;
      i = 10 * i + c - '0';
    }
    c = getc(f);
  }
  if (m)
    i = -i;
  *xp = (double) i / (double) iscale;

  while ((c = getc(f)) == ' ');
  j = m = frac = 0;
  while (isdigit(c) || c == '.' || c == '-') {
    if (c == '-') {
      m++;
      c = getc(f);
      continue;
    }
    if (c == '.')
      frac = 1;
    else {
      if (frac)
	jscale *= 10;
      j = 10 * j + c - '0';
    }
    c = getc(f);
  }
  if (m)
    j = -j;
  *yp = (double) j / (double) jscale;
  return (SUNFILE || i != -iscale || j != -jscale);
}
#endif	/* UW_FASTSCAN */

/* EOF */
