
/* "UNTIL", a graphics editor,
   Copyright (C) 1985, 1990 California Institute of Technology.
   Original authors: Glenn Gribble, port by Steve DeWeerth
   Unix Port Maintainer: John Lazzaro
   Maintainers's address: lazzaro@hobiecat.cs.caltech.edu;
                          CB 425 CU Boulder/Boulder CO 91125. 

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation (Version 1, 1989).

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
USA. */


/*******************************************************************************/
/*                                                                             */ 
/*  Glenn's own FF parser (mostly Telle's code), slightly changed.             */
/*  Caged_date='r{ $X by $u $]'                                                */
/*                                                                             */ 
/*  cleaned up by steve - 8 May 1990                                           */
/*                                                                             */ 
/*******************************************************************************/

#include <p2c/p2c.h>

#include "ffman.h"

#ifndef SYSDEVS_H
#include <p2c/sysdevs.h>
#endif

#ifndef MYLIB_H
#include <p2c/mylib.h>
#endif

#ifndef ASM_H
#include <p2c/asm.h>
#endif

#ifndef NEWASM_H
#include <p2c/newasm.h>
#endif

#ifndef NEWCI_H
#include <p2c/newci.h>
#endif

#ifndef MISC_H
#include <p2c/misc.h>
#endif

#include <p2c/newcrt.h>  /* steve */


#define file_edit_date	    1   /* <name>   */
#define grid_size	    2   /* <number> */
#define line_size	    3   /* <number> */
#define wing_size	    4   /* <number> */
#define default_color	    5   /* <number> */
#define smooth_delta_size   6   /* <number> */
#define window_for_smooth   7   /* <number> */
#define default_wing_angle  8   /* <real>   */
#define mbb                 9   /* <point> <point> */
#define frame_box          10   /* <point> <point> */
#define penMapFile         11   /* <name> */

#define ellipse_pie         1   /* boolean */

#define line_width	    1   /* <number> */
#define line_beg_arrow	    2   /* <number> */
#define line_end_arrow	    3   /* <number> */
#define line_wing_angle	    4   /* <real>   */
#define line_wing_len	    5   /* <number> */
#define line_bezier	    6
#define line_closed	    7
#define line_wing_width	    8   /* <number> */

#define text_center_place   1
#define text_font           2
#define text_char_scale     3
#define text_char_rot       4
#define text_char_slant     5

#define start_of_text     "`"

#define max_line_len       79


typedef enum {
  namestart, number, posnumber, ident, begbrack, endbrack, startparen,
  endparen, unknowntype
} toktype;


Static FILE *ff_file;   /* Input/output FF file */
Static long linenum;   /* Line number in FF file */
Static long cpos;   /* Current character position on line */
Static Char tokstart;
Static toktype lastoktype;
Static Char buffer[256];
Static short w_len;   /* current active default wing length */
Static double w_angle;   /* current active default wing angle */
Static boolean quit;   /* problem has occured */
Static short num_errors;   /* the number of errors in a read/write */
Static short clinelen;   /* write variables */
Static short lastColor;   /* Last color written */


/***********************************************************************/
/*                       Reading Routines                              */
/***********************************************************************/


Static void read_error(Char *s)
{
  if (XPOS > 0)
    putchar('\n');
  puts(buffer);
  printf("%*s^\n", (int)cpos, "");

  printf("Line: %ld Error: %s\n", linenum, s);
  num_errors++;
  printf("Press RETURN to continue:");
  scanf("%*[^\n]");
  getchar();
  printf("\015\t");
}


Static boolean getline(void)
{
  Char *TEMP;

  quit = false;
  if (P_eof(ff_file))
    quit = true;
  else {
    buffer[0] = '\0';
    while (!P_eof(ff_file) && *buffer == '\0') {
      fgets(buffer, 256, ff_file);
      TEMP = (char *) strchr(buffer, '\n');
      if (TEMP != NULL)
	*TEMP = 0;
      /* Deal with comment lines */
      if (*buffer != '\0' &&
	  (buffer[0] == ';' || buffer[0] == '#' || buffer[0] == '!'))
	buffer[0] = '\0';
      linenum++;
    }
    cpos = 1;
  }

  if (*buffer == '\0')
    quit = true;
  return (!quit);
}


Static void skip_blanks(void)
{
  while (cpos <= strlen(buffer) &&
	 (buffer[cpos - 1] == '\t' || buffer[cpos - 1] == '\n' ||
	  buffer[cpos - 1] == '\015' || buffer[cpos - 1] == ' '))
    cpos++;
}


Static boolean getInterestingLine(void)
{
  boolean q;

  if (cpos <= strlen(buffer))
    return true;

  if (!getline())
    return false;

  q = true;
  skip_blanks();
  while (cpos > strlen(buffer) && q) {
    q = getline();
    skip_blanks();
  }
  return (q == true);
}


Static boolean digit(Char c)
{
  return isdigit(c);
}


/*returns the type of the next token in the string - not updating the position cpos */
Static toktype nextoken(void)
{
  if (!getInterestingLine()) {
    lastoktype = unknowntype;
    return lastoktype;
  }

  skip_blanks();
  tokstart = buffer[cpos - 1];

  switch (tokstart) {

  case '`':
    lastoktype = namestart;
    break;

  case '-':
    lastoktype = number;
    break;

  case '(':
    lastoktype = startparen;
    break;

  case ')':
    lastoktype = endparen;
    break;

  case '[':
    lastoktype = begbrack;
    break;

  case ']':
    lastoktype = endbrack;
    break;

  default:
    if (isdigit(tokstart))
      lastoktype = posnumber;
    else
      lastoktype = ident;
    break;
  }

  return lastoktype;
}


Static void tok_alright(short incr)
{
  cpos += incr;
  skip_blanks();
}


/* Skip to the end of the current object, because we encountered an error. */
Static void error_scan_list(void)
{
  boolean q;

  q = getInterestingLine();
  while (q) {
    while (cpos <= strlen(buffer) && buffer[cpos - 1] != ')')
      cpos++;
    if (cpos > strlen(buffer))
      q = getInterestingLine();
    else
      q = false;
  }
}


Static boolean beginList(void)
{
  boolean Result;

  Result = true;
  if (nextoken() == startparen) {
    tok_alright(1);
    return Result;
  } else {
    read_error("Expecting start of list");
    error_scan_list();
    return false;
  }
  return Result;
}


/* Check for a given type of token */
Static boolean isThing(toktype thing)
{
  if (nextoken() == thing) {
    tok_alright(1);
    return true;
  } else
    return false;
}


Static boolean iS_End_List(void)
{
  return (isThing(endparen));
}


Static void Check_For_End_List(void)
{
  if (!iS_End_List()) {
    read_error("Expecting End of List");
    error_scan_list();
  }
}


Static boolean iS_Attribute_List(void)
{
  return (isThing(begbrack));
}


Static boolean is_Attribute_End(void)
{
  return (isThing(endbrack));
}


Static Char read_ident(void)
{
  if (isThing(ident))
    return tokstart;
  else {
    read_error("Expecting an identifier");
    error_scan_list();
    return ' ';
  }
}


Static long read_int(void)
{
  long Result;
  toktype t;
  long i;
  Char *STR1;

  t = nextoken();

  if (t == number || t == posnumber) {
    i = strtol(buffer + cpos - 1, &STR1, 10);
    cpos = STR1 - buffer + 1;
    Result = i;
    skip_blanks();
    return Result;
  }
  else {
    read_error("Expecting a number");
    error_scan_list();
    return 0;
  }
  return Result;
}


Static double read_real(void)
{
  double Result;
  toktype t;
  double r;
  Char *STR1;

  t = nextoken();

  if (t == number || t == posnumber) {
    r = strtod(buffer + cpos - 1, &STR1);
    cpos = STR1 - buffer + 1;
    Result = r;
    skip_blanks();
    return Result;
  }
  else {
    read_error("Expecting a real number");
    error_scan_list();
    return 0.0;
  }
  return Result;
}


Static long read_pos_int(void)
{
  long Result;
  toktype t;
  long i;
  Char *STR1;

  t = nextoken();

  if (t == number || t == posnumber) {
    i = strtol(buffer + cpos - 1, &STR1, 10);
    cpos = STR1 - buffer + 1;
  
    if (i < 0) {
      read_error("Expecting a positive number");
      i = -i;
    }

    skip_blanks();
    return i;
  }
  else {
    read_error("Expecting an integer");
    error_scan_list();
    return 0;
  }
}


Static point read_point(void)
{
  point p;

  p.x = read_int();
  p.y = read_int();
  return p;
}


Static mat_matrix read_matrix(void)
{
  mat_matrix m;

  m.a = read_int();
  m.b = read_int();
  m.c = read_int();
  m.d = read_int();
  m.e = read_int();
  m.f = read_int();
  m.g = read_int();
  return m;
}


/* Read a name, but I don't think I want to let the name break across lines?? */
Static Char *read_name(Char *Result)
{
  Char delim;
  short startpos;

  if (nextoken() == namestart) {
    cpos++;
    delim = buffer[cpos - 1];   /*get delimiting character*/
    cpos++;
    startpos = cpos;
    while (cpos <= strlen(buffer) && buffer[cpos - 1] != delim)
      cpos++;
    if (buffer[cpos - 1] == delim) {
      sprintf(Result, "%.*s", (int)(cpos - startpos), buffer + startpos - 1);
      cpos++;
    } else {
      read_error("Unterminated name");
      error_scan_list();
      strcpy(Result, "");
    }
    skip_blanks();
    return Result;
  } else {
    read_error("Expecting a Name");
    error_scan_list();
    return strcpy(Result, "");
  }
  return Result;
}


#define def_wing_length  1
#define def_sarrow_angle  1
#define def_color       1


Static void initialize_Read_data(void)
{
  *buffer = '\0';
  quit = false;
  num_errors = 0;
  lastoktype = unknowntype;
  tokstart = ' ';
  cpos = 1;
  linenum = 0;
  w_len = def_wing_length;
  w_angle = def_sarrow_angle;
}

#undef def_wing_length
#undef def_sarrow_angle
#undef def_color


/***********************************************************************/
/*                    Writing Routines                                 */
/***********************************************************************/

Static void write_out(Char *s)
{
  if (strlen(s) + clinelen > max_line_len && clinelen > 0) {
    putc('\n', ff_file);
    clinelen = 0;
  }
  fputs(s, ff_file);
  clinelen += strlen(s);
}


Static void write_eol(Char *s)
{
  write_out(s);
  putc('\n', ff_file);
  clinelen = 0;
}


Static void write_int(long i)
{
  Char s[21];
  long npos;

  sprintf(s, "%ld ", i);
  npos = strlen(s) + 1;
  write_out(s);
}


Static void write_real(double r)
{
  Char s[21];
  long npos;

  /*if (0<=r) and (r<1) then strwrite(s,1,npos,'0') else npos:=1;*/
  sprintf(s, "%1.5f ", r);
  npos = strlen(s) + 1;
  write_out(s);
}


Static void write_point(point p)
{
  Char s[51];
  long npos;

  sprintf(s, "%ld %ld ", p.x, p.y);
  npos = strlen(s) + 1;
  write_out(s);
}


Static void write_xy(long x, long y)
{
  Char s[51];
  long npos;

  sprintf(s, "%ld %ld ", x, y);
  npos = strlen(s) + 1;
  write_out(s);
}


Static void write_matrix(mat_matrix *m)
{
  write_int(m->a);
  write_int(m->b);
  write_int(m->c);
  write_int(m->d);
  write_int(m->e);
  write_int(m->f);
  write_int(m->g);
}


Static void write_delimited_name(Char delimiter, Char *s_)
{
  Char s[256];
  Char STR1[256];

  strcpy(s, s_);
  sprintf(s, "  %s", strcpy(STR1, s));
  s[0] = '`';
  s[1] = delimiter;
  strcat(s, "  ");
  s[strlen(s) - 2] = delimiter;
  write_out(s);
}
/* p2c: ffman.text, line 525: Note: Character >= 128 encountered [281] */


#define delims          "`\"'/\\!@#$%^&*\377"


Static void write_name(Char *s)
{
  short i;

  for (i = 1 ; i <= strlen(delims) && strposc(s, delims[i - 1], 1) != 0 ; i++);

  if (i <= strlen(delims))
    write_delimited_name(delims[i - 1], s);
  else {
    printf("\007WARNING: could not find a delimiter for string \"%s\".\n", s);
    write_delimited_name('`', s);
  }
}

#undef delims


Static void initialize_Write_data(void)
{
  clinelen = 0;
}


#define red             1
#define black           8


/***********************************************************************/
/*               Routines to read and write the data structures        */
/***********************************************************************/

Static short read_color(void)
{
  short Result, c;

  if (!beginList())
    return 4;
  c = read_pos_int();
  /*if (red<=c) and (c<=black) then read_color:=c*/
  if ((unsigned)c <= 15)
    Result = c;
  else {
    read_error("Undefined color type");
    Result = 4;
  }
  Check_For_End_List();
  return Result;
}

#undef red
#undef black


Static void write_color(short c)
{
  if (c == lastColor)
    return;
  write_out("O (");
  write_int(c);
  write_eol(")");
  lastColor = c;
}


Static object *read_box(void)
{
  object *Result;
  point ll, ur;

  if (!beginList())
    return NULL;
  ll = read_point();
  ur = read_point();
  Result = make_box(ll.x, ll.y, ur.x, ur.y);
  Check_For_End_List();
  return Result;
}


Static void write_box(pr_box_rec *b)
{
  write_out("B (");
  write_xy(b->x1, b->y1);
  write_xy(b->x2, b->y2);
  write_eol(")");
}


Static object *read_circle(void)
{
  object *Result;
  point c;
  long rad;

  if (!beginList())
    return NULL;
  c = read_point();
  rad = read_int();
  Result = make_circle(c.x, c.y, rad);
  Check_For_End_List();
  return Result;
}


Static void write_circle(pr_circle_rec *c)
{
  write_out("C (");
  write_xy(c->cx, c->cy);
  write_int(c->r);
  write_eol(")");
}


Static object *read_arc(void)
{
  object *Result;
  point c, f, l;
  long rad, start, length;

  if (!beginList())
    return NULL;
  c = read_point();
  f = read_point();
  l = read_point();
  rad = read_int();

  /* What are the start and end angles? */
  start = aTan2I(f.x - c.x, f.y - c.y);
  length = aTan2I(l.x - c.x, l.y - c.y) - start;

  Result = make_ellipse(c.x, c.y, rad, rad, start, length, 0);
  Check_For_End_List();
  return Result;
}


Static pnt_elem *read_points(void)
{
  pnt_elem *st, *cur;
  point p;

  p = read_point();
  st = make_pnt_elem(p.x, p.y);
  cur = st;
  while (!iS_End_List() && !quit) {
    p = read_point();
    cur->next = make_pnt_elem(p.x, p.y);
    cur = cur->next;
  }
  return st;
}


Static void write_points(pnt_elem *p)
{
  while (p != NULL) {
    write_xy(p->x, p->y);
    p = p->next;
  }
}


Static object *read_polygon(void)
{
  if (beginList())
    return (make_line(read_points(), l_polygon));
  else
    return NULL;
}


Static void write_polygon(pr_line_rec *p)
{
  write_out("P (");
  write_points(p->pnts);
  write_eol(")");
}


Static void write_dot(pr_dot_rec *d)
{
  write_out("D (");
  write_xy(d->x, d->y);
  write_eol(")");
}


Static object *read_dot(void)
{
  object *Result;
  point p;

  if (!beginList())
    return NULL;
  p = read_point();
  Result = make_dot(p.x, p.y);
  Check_For_End_List();
  return Result;
}


Static void write_ellipse(pr_ellipse_rec *e)
{
  write_out("E ");
  if (e->pie) {
    write_out("[");
    write_int(ellipse_pie);
    write_out("]");
  }
  write_out("(");

  write_xy(e->cx, e->cy);
  write_xy(e->a, e->b);
  write_xy(e->s, e->l);
  write_int(e->r);
  write_eol(")");
}


Static object *read_ellipse(void)
{
  object *Result;
  point c, r;
  long s, l, rot;
  boolean pie;
  object *o;

  pie = false;   /* not pie-segment */
  if (iS_Attribute_List()) {
    while (!is_Attribute_End() && !quit) {
      switch (read_pos_int()) {

      case ellipse_pie:
	pie = true;
	break;

      default:
	read_error("Undefined Ellipse Attribute");
	break;
      }
    }
  }
  if (!beginList())
    return NULL;
  c = read_point();
  r = read_point();
  s = read_int();
  l = read_int();
  rot = read_int();
  o = make_ellipse(c.x, c.y, r.x, r.y, s, l, rot);
  Result = o;
  o->okind.e.pie = pie;
  Check_For_End_List();
  return Result;
}


#define pi_             3.1415926536


Static object *read_line(void)
{
  boolean barrow, earrow, bez, lClosed;
  double wAngle;
  short ignore, lineWidth, wingLen, wingWidth;
  pnt_elem *pnts;

  lineWidth = 0;
  wingLen = 0;
  barrow = false;
  earrow = false;
  wAngle = 0.0;
  wingWidth = 0;
  bez = false;
  lClosed = false;

  if (iS_Attribute_List()) {
    while (!is_Attribute_End() && !quit) {
      switch (read_pos_int()) {

      case line_width:
	lineWidth = read_int();
	break;

      case line_beg_arrow:
	ignore = read_int();
	barrow = true;
	break;

      case line_end_arrow:
	ignore = read_int();
	earrow = true;
	break;

      case line_wing_angle:
	wAngle = read_real();
	break;

      case line_wing_len:
	wingLen = read_int();
	break;

      case line_bezier:
	bez = true;
	break;

      case line_closed:
	lClosed = true;
	break;

      case line_wing_width:
	wingWidth = read_int();
	break;

      default:
	read_error("Undefined Line Attribute");
	break;
      }
    }
  }
  if (beginList()) {
    if (wAngle != 0.0) {
      wingWidth = (long)floor(sin(wAngle * pi_ / 180) * wingLen + 0.5);
      wingLen = (long)floor(cos(wAngle * pi_ / 180) * wingLen + 0.5);
    }

    pnts = read_points();
    /*  wingLen:1,',',bArrow:1,',',eArrow:1,',',lClosed:1,',',bez:1,')');*/
    return (make_line0(pnts, lineWidth, wingWidth, wingLen, barrow, earrow,
		       lClosed, bez));
  } else
    return NULL;
}

#undef pi_


Static void write_2(long id, long data)
{
  write_int(id);
  write_int(data);
}


Static void write_ir(long id, double data)
{
  write_int(id);
  write_real(data);
}


Static void write_line(pr_line_rec *l)
{
  write_out("L ");

  if (l->lwid != 0 || l->h1 || l->h2 || l->closed || l->wwid != 0 ||
      l->wlen != 0 || l->curve) {
    write_out("[");
    if (l->lwid != 0)
      write_2(line_width, l->lwid);
    if (l->h1)
      write_2(line_beg_arrow, 1);
    if (l->h2)
      write_2(line_end_arrow, 1);
    if (l->closed)
      write_int(line_closed);
    if (l->wwid != 0)
      write_2(line_wing_width, l->wwid);
    if (l->wlen != 0)
      write_2(line_wing_len, l->wlen);
    if (l->curve)
      write_int(line_bezier);
    write_out("]");
  }

  write_out("(");
  write_points(l->pnts);
  write_eol(")");
}


Static long centerTypeToInt(centerType t)
{
  short n;

  switch (t) {

  case TLL:
    n = 1;
    break;

  case TLC:
    n = 2;
    break;

  case TLR:
    n = 3;
    break;

  case TCR:
    n = 4;
    break;

  case TUR:
    n = 5;
    break;

  case TUC:
    n = 6;
    break;

  case TUL:
    n = 7;
    break;

  case TCL:
    n = 8;
    break;

  case TCC:
    n = 9;
    break;
  }
  return n;
}


Static centerType read_center_type(void)
{
  centerType p;

  p = TLL;
  switch (read_pos_int()) {

  case 1:
    p = TLL;
    break;

  case 2:
    p = TLC;
    break;

  case 3:
    p = TLR;
    break;

  case 4:
    p = TCR;
    break;

  case 5:
    p = TUR;
    break;

  case 6:
    p = TUC;
    break;

  case 7:
    p = TUL;
    break;

  case 8:
    p = TCL;
    break;

  case 9:
    p = TCC;
    break;
  }
  return p;
}


Static object *read_alpha(void)
{
  object *Result;
  Char n[256];
  point p;
  object *o;

  curTextOrig = def_center_type;
  curTextFont = def_font;
  curTextRot = 0;
  curTextScale = def_font_scale;
  curTextSlant = 0;

  if (iS_Attribute_List()) {
    while (!is_Attribute_End() && !quit) {
      switch (read_pos_int()) {

      case text_center_place:
	curTextOrig = read_center_type();
	break;

      case text_font:
	curTextFont = read_pos_int();
	break;

      case text_char_scale:
	curTextScale = read_pos_int();
	break;

      case text_char_rot:
	curTextRot = read_int();
	break;

      case text_char_slant:
	curTextSlant = read_int();
	break;

      default:
	read_error("Undefined alpha attribute");
	break;
      }
    }
  }

  if (!beginList())
    return NULL;
  read_name(n);
  p = read_point();
  o = make_text(p.x, p.y, n);
  Result = o;
  if (nextoken() == namestart) {
    read_name(n);
    strcpy(o->okind.t.TeXwords, n);
  }
  Check_For_End_List();
  return Result;
}


Static void write_alpha(pr_text_rec *a)
{
  write_out("T [");
  if (a->orig != def_center_type)
    write_2(text_center_place, centerTypeToInt(a->orig));
  /* if a.font<>def_font then */
  write_2(text_font, a->font);
  if (a->scale != def_font_scale)
    write_2(text_char_scale, a->scale);
  if (a->rot != 0)
    write_2(text_char_rot, a->rot);
  if (a->slant != 0)
    write_2(text_char_slant, a->slant);
  write_out("](");
  write_name(a->words);
  write_xy(a->x, a->y);
  write_name(a->TeXwords);
  write_eol(")");
}


/* <Instance>    := 'I' <blank> '(' <name> <matrix> ')'          */
/*                       ( figure name, display matrix ) */
/* <Instance>    := 'i' <blank> '(' <name> <rotate> <tran-point>         */
/*                               <scale-x> <scale-y> <scale-num> ')'     */
/*              ( figure name, rotate in degrees, translation, scaling ) */
Static object *read_instance(boolean useMatrix)
{
  mat_matrix mat;
  Char n[256];
  object *o;
  figure *f;

  if (!beginList())
    return NULL;
  read_name(n);

  f = find_figure(n);

  if (f != NULL) {
    if (useMatrix) {
      mat = read_matrix();
      o = make_instance(f->guts->okind.i.contents, mat, true);
      o->okind.i.matOnly = true;
    }
    else {
      o = make_instance(f->guts->okind.i.contents, mat_ident_mat, true);
      o->okind.i.rot = read_int();
      o->okind.i.tx = read_int();
      o->okind.i.ty = read_int();
      o->okind.i.sx = read_int();
      o->okind.i.sy = read_int();
      o->okind.i.den = read_int();
      o->okind.i.matOnly = false;
      set_instance_mat(o);
    }
  }
  else {  /* figure doesn't exist */
    o = NULL;

    if (!useMatrix) {  /* clear out rest of line */
      read_int();
      read_int();
      read_int();
      read_int();
      read_int();
      read_int();
    }
  }

  Check_For_End_List();
  return o;
}


Static void write_instance(object *o)
{
  /*writeln('write_instance: "',o.i.contents^.base.parent^.name,'"');*/
  if (o->okind.i.matOnly) {
    write_out("I (");
    write_name(o->okind.i.contents->okind.base.parent->name);
    write_matrix(&o->okind.i.m);
  } else {
    write_out("i (");
    write_name(o->okind.i.contents->okind.base.parent->name);
    write_int(o->okind.i.rot);
    write_int(o->okind.i.tx);
    write_int(o->okind.i.ty);
    write_int(o->okind.i.sx);
    write_int(o->okind.i.sy);
    write_int(o->okind.i.den);
  }
  write_eol(")");
}


/***********************************************************************/
/*                       Read/Write a Figure                           */
/***********************************************************************/

Static figure *read_figure(void)
{
  figure *f;
  boolean still_more;
  object *s, *list_shapes;
  long fig_def_color;
  double ignore;
  point ignorePt;
  Char STR1[256];

  fig_def_color = 4;
  if (read_ident() != 'F')
    read_error("Expecting a figure");

  f = make_figure(read_name(STR1));
  printf("\n             figure: %s...", f->name);
  if (iS_Attribute_List()) {
    while (!is_Attribute_End() && !quit) {
      switch (read_pos_int()) {

      case file_edit_date:
	read_name(f->edit_date);
	break;

      case penMapFile:
	read_name(f->mapFile);
	strcpy(curFile->defMapFile, f->mapFile);
	break;

      case grid_size:
	f->grid = read_pos_int();
	break;

      case line_size:
	f->linewidth = read_pos_int();
	break;

      case wing_size:
	f->wing_length = read_pos_int();
	w_len = f->wing_length;
	break;

      case default_color:
	fig_def_color = read_pos_int();
	break;

      case smooth_delta_size:
	ignore = read_pos_int();
	break;

      case window_for_smooth:
	ignore = read_pos_int();
	break;

      case default_wing_angle:
	ignore = read_real();
	w_angle = ignore;
	break;

      case mbb:
	ignorePt = read_point();
	ignorePt = read_point();
	break;

      case frame_box:
	ignorePt = read_point();
	ignorePt = read_point();
	break;

      default:
	read_error("Undefined figure attribute");
	break;
      }
    }
  }

  if (beginList()) {
    still_more = true;
    list_shapes = NULL;
    while (still_more && !iS_End_List() && !quit) {
      switch (read_ident()) {

      case 'O':
	curColor = read_color();
	break;

      case 'B':
	s = read_box();
	break;

      case 'C':
	s = read_circle();
	break;

      case 'A':
	s = read_arc();
	break;

      case 'E':
	s = read_ellipse();
	break;

      case 'D':
	s = read_dot();
	break;

      case 'P':
	s = read_polygon();
	break;

      case 'L':
	s = read_line();
	break;

      case 'T':
	s = read_alpha();
	break;

      case 'I':
	s = read_instance(true);
	break;

      case 'i':
	s = read_instance(false);
	break;

      default:
	read_error("Unknown shape type");
	still_more = false;
	error_scan_list();
	break;
      }
    }  /* of the list of shapes - found end of figure */
  }  /* of beginlist */
  curColor = fig_def_color;   /* Restore this value */

  /* Make the mbb's correct here. */
  set_base_mbb(f->guts->okind.i.contents, false);
  set_instance_mbb(f->guts, false);

  return f;
}


Static Char *primkinds_NAMES[] = {
  "PR_NONE", "PR_DOT", "PR_LINE", "PR_BOX", "PR_CIRCLE", "PR_ELLIPSE",
  "PR_NGON", "PR_TEXT", "PR_INST", "PR_LAST"
} ;


Static void write_figure(figure *f)
{
  object *r, *p;

  printf("\n             figure: %s...", f->name);
  if (f->changes != 0)
    today(f->edit_date);
  write_out("F ");
  write_name(f->name);
  write_out("[");
  if (*f->edit_date != '\0') {
    write_int(file_edit_date);
    write_name(f->edit_date);
  }

  /* Always write out the mapfile */
  write_int(penMapFile);
  write_name(f->mapFile);

  write_eol("](");
  r = f->guts->okind.i.contents;
  p = r->next;
  lastColor = -1;

  while (p != r) {
    if (p->kind == o_base)
      printf("          WARNING: attempt to write_out an o_base.\n");
    else {
      write_color(p->color.value);

      switch (p->kind) {

      case pr_none:
      case pr_last:
	printf("\007          Bad write_object of %s\n",
	       primkinds_NAMES[(long)p->kind]);
	break;

      case pr_ngon:
	printf("\007          Saving of %s not supported.\n",
	       primkinds_NAMES[(long)p->kind]);
	break;

      case pr_dot:
	write_dot(&p->okind.d);
	break;

      case pr_ellipse:
	write_ellipse(&p->okind.e);
	break;

      case pr_line:
	write_line(&p->okind.l);
	break;

      case pr_box:
	write_box(&p->okind.b);
	break;

      case pr_circle:
	write_circle(&p->okind.c);
	break;

      case pr_text:
	write_alpha(&p->okind.t);
	break;

      case pr_inst:
	write_instance(p);
	break;

      default:
	printf("Illegal PRKIND=%d\n", (int)p->kind);
	break;
      }/* case PRKIND */
    }

    p = p->next;
  }
  write_eol(")");
}


/***********************************************************************/
/*               Read In a File                                        */
/***********************************************************************/

Static boolean start_Read_operation(Char *s)
{
  boolean Result;
  Char t[256];

  newci_fixfname(s, "ff", "");

  TRY(try1);
    if (ff_file != NULL)
      ff_file = freopen(s, "r", ff_file);
    else
      ff_file = fopen(s, "r");
    if (ff_file == NULL) {
      P_escapecode = -10;
      P_ioresult = FileNotFound;
      goto _Ltry1;
    }
    printf("          Reading from %s", s);
    initialize_Read_data();
    Result = true;

  RECOVER2(try1,_Ltry1);
    Result = false;
    if (P_escapecode != -10)
      misc_printerror(P_escapecode, P_ioresult);
    else {
      misc_getioerrmsg(t, P_ioresult);
      printf("Can't read file %s because %s\n", s, t);
    }
  ENDTRY(try1);
  return Result;
}


fileSpec *read_in_a_file(Char *s_)
{
  fileSpec *Result;
  Char s[256];
  fileSpec *fs;
  figure *f;

  strcpy(s, s_);
  if (start_Read_operation(s)) {
    fs = make_filespec(s, established);
    while (getline())   /*still more stuff out there*/
      f = read_figure();
    Result = fs;
    if (ff_file != NULL)
      fclose(ff_file);
    ff_file = NULL;
    if (num_errors > 0)
      printf("          WARNING: Found %d errors in file\n", num_errors);
  } else
    Result = NULL;
  putchar('\n');
  return Result;
}


/***********************************************************************/
/*                       Write Out A File                              */
/***********************************************************************/

Static boolean start_Write_operation(Char *s)
{
  boolean Result;
  Char t[256];

  TRY(try2);
    if (ff_file != NULL)
      ff_file = freopen(s, "w", ff_file);
    else
      ff_file = fopen(s, "w");
    if (ff_file == NULL) {
      P_escapecode = -10;
      P_ioresult = FileNotFound;
      goto _Ltry2;
    }
    printf("          Writing to %s", s);
    initialize_Write_data();
    Result = true;
  RECOVER2(try2,_Ltry2);
    Result = false;
    if (P_escapecode != -10)
      misc_printerror(P_escapecode, P_ioresult);
    else {
      misc_getioerrmsg(t, P_ioresult);
      printf("Can't write file %s because %s\n", s, t);
    }
  ENDTRY(try2);
  putchar('\n');
  return Result;
}


/* mark all figures inactive and mark which file they belong to */
Static void mark_figs(fileSpec *fs)
{
  figure *figp;
  fileSpec *filp;

  filp = files;
  while (filp != NULL) {
    figp = filp->figures;
    while (figp != NULL) {
      figp->active = false;
      figp->written = false;
      figp->thisfile = (filp == fs);
      figp = figp->next;
    }
    filp = filp->next;
  }
}


Static void write_fig(figure *f, boolean thisFile)
{
  object *op;

  if (f->active) {
    printf(
      "\007\201ERROR:\200 Recursive call to %s, file must be hand-editted to fix.\n",
      f->name);
    num_errors++;
    return;
  }
  if (f->written)
    return;
  f->active = true;
  /* writeln('Checking: ',f^.name); */
  if (f->thisfile && !thisFile) {
    printf("\007\201ERROR:\200 %s is called by a figure from another file\n",
	   f->name);
    printf(" which is called by a figure from this file.\007\n");
    num_errors++;
  }

  op = f->guts->okind.i.contents->next;
  while (op->kind != o_base) {
    if (op->kind == pr_inst)
      write_fig(op->okind.i.contents->okind.base.parent, f->thisfile);
    op = op->next;
  }

  if (f->thisfile) {
    write_figure(f);
    f->changes = 0;
  }

  f->written = true;   /* So we don't look at it again. */
  f->active = false;
}


void write_out_the_File(fileSpec *fs)
{
  figure *f;

  if (!start_Write_operation(fs->fileName))
    return;

  num_errors = 0;
  mark_figs(fs);
  f = fs->figures;
  while (f != NULL) {
    write_fig(f, true);
    f = f->next;
  }

  if (ff_file != NULL)
    fclose(ff_file);

  ff_file = NULL;
  if (num_errors > 0) {
    printf("There were %d errors during the writing of%s\n", num_errors, fs->fileName);
    printf("You should look at your file before exiting.\n");
    printf("Waiting for return to continue:");
    scanf("%*[^\n]");
    getchar();
  }

  fs->status = established;
}

