/***********************************************************************
     "support.o": generic SUPPORT functions.
***********************************************************************/

/***********************************
  (C) Copyright 1992-1993; dit/upm
   Distributed under the conditions stated in the
   TOPO General Public License (see file LICENSE)
 ***********************************
 $Log: support.c,v $
 * Revision 1.5  1994/11/08  13:49:06  lotos
 * *** empty log message ***
 *
 * Revision 1.4  1993/10/19  18:32:32  lotos
 * ported to BSD/386
 *
 * Revision 1.3  1993/08/02  17:00:19  lotos
 * fix declaration 0f _draw_node (warning)
 *
 * Revision 1.2  1993/03/29  18:14:34  lotos
 * new AST drawing
 *
 * Revision 1.1  1993/03/18  11:14:24  lotos
 * Initial revision
 *
 ***********************************/

#ifndef lint
static char rcsid[]= "$Id: support.c,v 1.5 1994/11/08 13:49:06 lotos Exp $";
#endif

#define support_IMP

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "glad.hh"
#include "support.hh"

PRIVATE void _draw_node ();

/*
 *** Symbol table handling functions ***
 */

PRIVATE int
CD_cmp (p1, p2)
  tSYM *p1, *p2;
{
  if (p1->name == p2->name)
    return 0;
  else if (p1->name == NULL)
    return -1;
  else if (p2->name == NULL)
    return 1;
  else
    return strcmp(p1->name, p2->name);
}

/* case independent strcmp */

#define iscapital(chr)	(isascii(chr) && isupper(chr))
#define tosmall(chr)	(iscapital(chr)? tolower(chr): (chr))

PRIVATE int
ccstrcmp (s1, s2)
  char *s1, *s2;
{
  register char c1, c2;

  for (; *s1 != '\0'; ++s1, ++s2)
  { if (*s2 == '\0')
      return 1;
    c1= tosmall(*s1);
    c2= tosmall(*s2);
    if (c1 < c2)
      return -1;
    else if (c1 > c2)
      return 1;
  }
  if (*s2 == '\0')
    return 0;
  else
    return -1;
}

PRIVATE int
CI_cmp (p1, p2)
  tSYM *p1, *p2;
{
  if (p1->name == p2->name)
    return 0;
  else if (p1->name == NULL)
    return -1;
  else if (p2->name == NULL)
    return 1;
  else
    return ccstrcmp(p1->name, p2->name);
}

PUBLIC void
sort_sym (tbl, strict)
  tSYM tbl[];
  int strict;
{
  int size;
  int (*cmp)();

  abort_if(tbl[0].name == NULL ||
	   tbl[0].type == 0)
  for (size= 0; tbl[size].name != NULL; ++size)
    ;
  abort_if(tbl[size].type != 0)
  ++size;
  if (strict)
    cmp= CD_cmp;
  else
    cmp= CI_cmp;
  (void) qsort(ALIN(char, tbl), size, sizeof(tSYM), cmp);
  abort_if(tbl[0].name != NULL &&
	   tbl[0].type != 0)
  if (strict)
    tbl[0].type= size;
  else
    tbl[0].type= -size;
}

PUBLIC int
find_sym (sym, tbl)
  char *sym;
  tSYM tbl[];
{
  unsigned size;
  tSYM sym_buf;
  tSYM *sym_ptr;
  int (*cmp)();

  abort_if(tbl[0].name != NULL ||
	   tbl[0].type == 0)
  if (tbl[0].type > 0)
  { size= tbl[0].type;
    cmp= CD_cmp;
  }
  else
  { size= -tbl[0].type;
    cmp= CI_cmp;
  }
  abort_if(sym == NULL)
  sym_buf.name= sym;
  sym_ptr= ALIN(tSYM, (char*) bsearch(ALIN(char, &sym_buf),
			      ALIN(char, tbl),
			      size, sizeof(tSYM), cmp));
  if (sym_ptr == NULL)
    return 0;
  else
    return sym_ptr->type;
}


/*
 *** Node queue manipulation functions ***
 */

PUBLIC NDqueue
NQcreate ()
{
  NDqueue queue;

  talloc(queue, 1);
  queue->mark.next= queue->mark.prev= NULL;
  queue->mark.data= NULL;
  queue->first= queue->last= &queue->mark;
  return queue;
}

PUBLIC NDlink
NQfind (queue, type)
  NDqueue queue;
  int type;
{
  NDlink link;

  abort_if(queue == NULL ||
	   type >= 0)
  abort_if(queue->first == NULL)
  for (link= queue->first; link->next != NULL; link= link->next)
  { abort_if(link->data == NULL)
    if (link->data->type == type)
      return link;
  }
  return NULL;
}

PUBLIC NDlink
NQadd (queue, nodep)
  NDqueue queue;
  TNODE *nodep;
{
  NDlink link;

  abort_if(queue == NULL ||
	   nodep == NULL)
  talloc(link, 1);
  link->data= nodep;
  abort_if(queue->last == NULL)
  if (queue->last->next == NULL)	/* no element */
  { abort_if(queue->first != queue->last)
    link->next= link->prev= queue->last;
    queue->first= queue->last= link;
  }
  else					/* one element at least */
  { link->next= queue->last->next;
    link->prev= queue->last;
    queue->last->next= link;
    queue->last= link;
  }
  return link;
}

PUBLIC void
NQrm (link)
  NDlink link;
{
  abort_if(link == NULL)
  abort_if(link->next == NULL ||
	   link->prev == NULL)
  if (link->next->next == NULL &&
      link->prev->prev == NULL)		/* the only one element */
  { NDqueue queue;

    abort_if(link->next != link->prev)
    queue= (NDqueue) link->prev;
    queue->first= queue->last= &queue->mark;
  }
  else if (link->next->next == NULL)	/* last element */
  { NDqueue queue;

    queue= (NDqueue) link->next;
    queue->last= link->prev;
    queue->last->next= link->next;
  }
  else if (link->prev->prev == NULL)	/* first element */
  { NDqueue queue;

    queue= (NDqueue) link->prev;
    queue->first= link->next;
    queue->first->prev= link->prev;
  }
  else					/* an element in the middle */
  { link->prev->next= link->next;
    link->next->prev= link->prev;
  }
  tfree(link);
}

PUBLIC NDqueue
NQdup (queue)
  NDqueue queue;
{
  NDlink link;
  NDqueue new;

  abort_if(queue == NULL)
  abort_if(queue->first == NULL)
  new= NQcreate();
  for (link= queue->first; link->next != NULL; link= link->next)
    (void) NQadd(new, link->data);
  return new;
}

PUBLIC void
NQdestroy (queue)
  NDqueue queue;
{
  NDlink link;

  abort_if(queue == NULL)
  abort_if(queue->first == NULL)
  if (queue->first->next != NULL)
  { for (link= queue->first->next; link->next != NULL; link= link->next)
      tfree(link->prev);
    abort_if(queue->last == NULL ||
	     queue->last->next == NULL)
    tfree(queue->last);
  }
  tfree(queue);
}

PUBLIC void
NQdraw (fp, val)
  FILE *fp;
  CLR_TYPE val;
{
  NDqueue queue;
  NDlink link;
  void _draw_node();

  queue= (NDqueue) val;
  link= NQfirst(queue);
  if (link != NULL)
  { (void) fprintf(fp, "\n");
    do
      _draw_node(fp, NQdata(link));
    while ((link= NQnext(link)) != NULL);
  }
}


/*
 *** Basic data and functions for using EPS ***
 */

typedef struct
{ struct
  { void (*draw)();
    void (*back)();
  } *item;
  int size;
  int lock;
} tDRAW_TBL;	/* drawing table structure */

/* drawing table */
PRIVATE tDRAW_TBL draw_tbl= { NULL, 0, FALSE };

/* initializes drawing table */

PRIVATE void
draw_init ()
{
  int i, j;

  abort_if(draw_tbl.size != 0)
  for (i= 0; iocolours[i].keyword != NULL; ++i)
  { j= iocolours[i].atype;
    abort_if(j < 0)
    if (j > draw_tbl.size)
      draw_tbl.size= j;
  }
  ++draw_tbl.size;
  talloc(draw_tbl.item, draw_tbl.size);
  for (i= 0; i < draw_tbl.size; ++i)
    draw_tbl.item[i].draw= NULL;
  abort_if(draw_tbl.size == 0)
}

/* starts drawing */

PRIVATE void
draw_start ()
{
  int i, j;

  abort_if(draw_tbl.size == 0)
  abort_if(draw_tbl.lock)
  for (i= 0; iocolours[i].keyword != NULL; ++i)
  { j= iocolours[i].atype;
    abort_if(j < 0 || j >= draw_tbl.size)
    draw_tbl.item[j].back= iocolours[i].put;
    if (draw_tbl.item[j].draw != NULL)
      iocolours[i].put= draw_tbl.item[j].draw;
  }
  draw_tbl.lock= TRUE;
}

/* stops drawing */

PRIVATE void
draw_stop ()
{
  int i, j;

  abort_if(draw_tbl.size == 0)
  abort_if(!draw_tbl.lock)
  for (i= 0; iocolours[i].keyword != NULL; ++i)
  { j= iocolours[i].atype;
    abort_if(j < 0 || j >= draw_tbl.size)
    iocolours[i].put= draw_tbl.item[j].back;
  }
  draw_tbl.lock= FALSE;
}

PUBLIC void
set_draw (attr, func)
  int attr;
  void (*func)();
{
  abort_if(draw_tbl.size == 0)
  abort_if(draw_tbl.lock)
  abort_if(attr < 0 ||
	   attr >= draw_tbl.size ||
	   draw_tbl.item[attr].draw != NULL)
  abort_if(func == NULL)
  draw_tbl.item[attr].draw= func;
}

#ifndef PRETTY
PRIVATE char *
node2name (type)
 int type;
{
  static char buf[100];

  (void) sprintf(buf, "%d", type);
  return &buf[0];
}
#endif

/* draws a node */

PRIVATE void
_draw_node (fp, nodep)
  FILE *fp;
  TNODE *nodep;
{
  char *node2name();

  if (nodep == NULL)
    return;
  (void) fprintf(fp, "<%s>", node2name(nodep->type));
  putattrs(fp, nodep);
}

PUBLIC void
draw_node (fp, nodep)
  FILE *fp;
  TNODE *nodep;
{
  draw_start();
  _draw_node(fp, nodep);
  draw_stop();
}

/* draws a tree */

PRIVATE void
_draw_tree (fp, level, tree)
  FILE *fp;
  int level;
  TNODE *tree;
{
  int i;
  
  if (tree == NULL)
    return;
  for (i= level; i > 0; --i)
    (void) fprintf(fp, "  ");
  _draw_node(fp, tree);
  _draw_tree(fp, level + 1, gt_fs(tree));
  _draw_tree(fp, level, gt_rb(tree));
}

PUBLIC void
draw_tree (fp, tree)
  FILE *fp;
  TNODE *tree;
{
  draw_start();
  _draw_tree(fp, 0, tree);
  draw_stop();
}


/*
 *** General functions for handling attributes ***
 */

/* initializes attribute handling */

PRIVATE void
attr_init ()
{
  set_draw(c_opns, type2clr(c_opns)->put);
  type2clr(c_opns)->put= NULL;
  type2clr(c_opns)->get= NULL;
  type2clr(c_antv)->free= NULL;
  type2clr(c_antv)->cpy= NULL;
  set_draw(c_accept, NQdraw);
  set_draw(c_reject, NQdraw);
  set_draw(c_remain, NQdraw);
  set_draw(c_insert, NQdraw);
}

/* provides the value of an attribute */

PUBLIC CLR_TYPE
_attr_value (nodep, attr)
  TNODE *nodep;
  int attr;
{
  TATTR *p;

  p= find_attr(attr, nodep);
  abort_if(p == NULL)
  return p->value;
}

/* provides the value of an attribute, or else "not_found_value" */

PUBLIC CLR_TYPE
_find_attr_value (nodep, attr, not_found_value)
  TNODE *nodep;
  int attr;
  CLR_TYPE not_found_value;
{
  TATTR *p;

  abort_if(nodep == NULL)
  p= find_attr(attr, nodep);
  if (p == NULL)
    return not_found_value;
  else
    return p->value;
}

/* adds an attribute and its value */

PUBLIC void
_add_attr_value (nodep, attr, val)
  TNODE *nodep;
  int attr;
  CLR_TYPE val;
{
  TATTR *p;

  p= add_attr(attr, nodep);
  abort_if(p == NULL)
  p->value= val;
}

/* removes an attribute and its value */

PUBLIC void
_del_attr_value (nodep, attr)
  TNODE *nodep;
  int attr;
{
  TATTR *p;

  p= take_attr(attr, nodep);
  abort_if(p == NULL)
  free_attr(p);
}

/* removes an attribute, if any, and its value */

PUBLIC void
_fdel_attr_value (nodep, attr)
  TNODE *nodep;
  int attr;
{
  TATTR *p;

  abort_if(nodep == NULL)
  p= take_attr(attr, nodep);
  if (p != NULL)
    free_attr(p);
}


/* like "_attr_value()", but for attribute table */

PUBLIC CLR_TYPE
_AT_attr_value (i, attr)
  int i;
  int attr;
{
  TATTR *p;

  abort_if(!is_stbl_entry(ATable, i))
  p= ATfind(ATable, i, attr);
  abort_if(p == NULL)
  return p->value;
}

/* like "_find_attr_value()", but for attribute table */

PUBLIC CLR_TYPE
_AT_find_attr_value (i, attr, not_found_value)
  int i;
  int attr;
  CLR_TYPE not_found_value;
{
  TATTR *p;

  abort_if(!is_stbl_entry(ATable, i))
  p= ATfind(ATable, i, attr);
  if (p == NULL)
    return not_found_value;
  else
    return p->value;
}

/* like "_add_attr_value()", but for attribute table */

PUBLIC void
_AT_add_attr_value (i, attr, val)
  int i;
  int attr;
  CLR_TYPE val;
{
  TATTR *p;

  p= ATadd(ATable, i, attr);
  abort_if(p == NULL)
  p->value= val;
}

/* like "_del_attr_value()", but for attribute table */

PUBLIC void
_AT_del_attr_value (i, attr)
  int i;
  int attr;
{
  TATTR *p;

  abort_if(!is_stbl_entry(ATable, i))
  p= ATtake(ATable, i, attr);
  abort_if(p == NULL)
  free_attr(p);
}

/* like "_fdel_attr_value()", but for attribute table */

PUBLIC void
_AT_fdel_attr_value (i, attr)
  int i;
  int attr;
{
  TATTR *p;

  abort_if(!is_stbl_entry(ATable, i))
  p= ATtake(ATable, i, attr);
  if (p != NULL)
    free_attr(p);
}


/*
 *** Annotation data and functions ***
 */

/* initializes annotation information list */

PRIVATE void
annot_list_init ()
{
  tANNOT *ant_1st;
  tANNOT *ant_ptr;
  int ant_grp;
  int ant_cnt;
  int i;

  abort_if(_annot_list_size != 0)
  for (ant_1st= _annot_list; ant_1st->name != NULL; ++ant_1st)
  { ++_annot_list_size;
    /* a new group was found? */
    if (ant_1st->group.size == 0)
      continue;
    if (ant_1st->group.item != NULL)
      continue;
    /* yes, it was! */
    ant_grp= ant_1st->group.size;
    /* calculate the size of the group */
    ant_cnt= 1;
    for (ant_ptr= ant_1st + 1; ant_ptr->name != NULL; ++ant_ptr)
      if (ant_ptr->group.size == ant_grp)
	++ant_cnt;
    abort_if(ant_cnt == 0)
    /* initialize the first annotation of the group */
    ant_1st->group.size= ant_cnt;
    talloc(ant_1st->group.item, ant_cnt);
    i= 0;
    ant_1st->group.item[i++]= ant_1st;
    for (ant_ptr= ant_1st + 1; ant_ptr->name != NULL; ++ant_ptr)
      if (ant_ptr->group.size == ant_grp)
	ant_1st->group.item[i++]= ant_ptr;
    abort_if(i != ant_cnt)
    /* find and initialize the remainder annotations of the group */
    for (ant_ptr= ant_1st + 1; ant_ptr->name != NULL; ++ant_ptr)
      if (ant_ptr->group.size == ant_grp)
      { abort_if(ant_ptr->group.item != NULL)
	ant_ptr->group.size= ant_cnt;
	ant_ptr->group.item= ant_1st->group.item;
      }
  }
  abort_if(_annot_list_size == 0)
}

/* initializes node index to annotation information */

PRIVATE void
annot_node_init ()
{
  tANNOT *ant_ptr;
  int i;

  abort_if(_annot_node.last != 0)
  for (ant_ptr= annot_list(0); ant_ptr->name != NULL; ++ant_ptr)
  { abort_if(ant_ptr->node >= 0)
    if (ant_ptr->node < _annot_node.last)
       _annot_node.last= ant_ptr->node;
  }
  abort_if(_annot_node.last == 0)
  --_annot_node.last;
  talloc(_annot_node.item, -_annot_node.last);
  _annot_node.item+= -_annot_node.last - 1;
  for (i= 0; i > _annot_node.last; --i)
    _annot_node.item[i]= NULL;
  for (ant_ptr= annot_list(0); ant_ptr->name != NULL; ++ant_ptr)
    _annot_node.item[ant_ptr->node]= ant_ptr;
}

/* initializes attribute index to annotation information */

PRIVATE void
annot_attr_init ()
{
  tANNOT *ant_ptr;
  int i;

  abort_if(_annot_attr.last != 0)
  for (ant_ptr= annot_list(0); ant_ptr->name != NULL; ++ant_ptr)
  { abort_if(ant_ptr->attr <= 0)
    if (ant_ptr->attr > _annot_attr.last)
      _annot_attr.last= ant_ptr->attr;
  }
  abort_if(_annot_attr.last == 0)
  ++_annot_attr.last;
  talloc(_annot_attr.item, _annot_attr.last);
  for (i= 0; i < _annot_attr.last; ++i)
    _annot_attr.item[i]= NULL;
  for (ant_ptr= annot_list(0); ant_ptr->name != NULL; ++ant_ptr)
    _annot_attr.item[ant_ptr->attr]= ant_ptr;
}

/* initializes I/O information of annotation attributes */

PRIVATE void
annot_io_init ()
{
  tANNOT *ant_ptr;

  for (ant_ptr= annot_list(0); ant_ptr->name != NULL; ++ant_ptr)
  { type2clr(ant_ptr->attr)->free= NULL;
    type2clr(ant_ptr->attr)->cpy= NULL;
  }
}


/*
 *** Support starting and stopping functions ***
 */

PUBLIC void
xtart ()
{
  cast_init(iocolours);
  draw_init();
  attr_init();
  annot_list_init();
  annot_node_init();
  annot_attr_init();
  annot_io_init();
}

PUBLIC void
stop (errcnt)
  int errcnt;
{
  if (errcnt > 0)
    if (errcnt == 1)
      fail("1 error found");
    else
      fail("%d errors found", errcnt);
  exit(0);
}
