/* This file is part of the Project Athena Zephyr Notification System.
 * It contains functions for the windowgram COMPILER rope structure.
 * A rope is a thick string.
 *
 *	Created by:	Mark W Eichin
 *
 *	$Source: /mit/zephyr/src/zwgc/RCS/ropes.c,v $
 *	$Author: jtkohl $
 *
 *	Copyright (c) 1987 by the Massachusetts Institute of Technology.
 *	For copying and distribution information, see the file
 *	"mit-copyright.h". 
 */

#include <zephyr/mit-copyright.h>

#ifndef lint
static char rcsid_ropes_c[] = "$Header: ropes.c,v 2.10 89/05/30 22:44:27 jtkohl Exp $";
#endif lint

/* #include "wgcomp.h" */
#include "ropes.h"
#include "support.h"
#include <ctype.h>
#include "comp_types.h"
char *null_strand="";

char *get_strand(r,ro)
     rope *r;
     int ro;
{
  switch(r->typ)
    {
    case VAR:
      printf("zwgc:ropes get_strand on VAR:aborting.\n");
      abort();
      break;
    case FIELD:
      if (r->ind.ex < strand_field_max)
	  return &strand_field[r->ind.ex][ro];
      else
	  return null_strand;
      break;
    case HEAD:
      if (r->ind.ex <= strand_head_max)
	  return &strand_head[r->ind.ex][ro];
      else
	  return null_strand;
      break;
    case RAW:
      if (r->ind.ex < max_raws)
	  return &raw_table[r->ind.ex][ro];
      else
	  return null_strand;
      break;
    case SHOW:
      return null_strand;
      break;
    case ROPE:
    case COMMAND:
    case NEXT:
    case SYNC:
    case FORM:
    case END:
      /* maybe we should just return null string... */
      printf("zwgc:ropes Ungettable strand(%d)\n",r->typ);
      abort();
    default:
      printf("zwgc:ropes Enumeration failure?(%d)\n",r->typ);
      abort();
    }
}

/* This is an internal typedef, not exported beyond ropes.c */
typedef enum { MS_FAIL, MS_SUCCESS, MS_FIRST, MS_SECOND, MS_WILD } ms_ret;
/* caseless_eq(a,b) 
   note here that we don't care if toupper does wierd things on upper
   case letters, unless it just maps them to a single lowercase one
   (which becomes a bug, but is unlikely to trigger a whole string)
   since it IS on an alpha, already known to be ascii... in any case,
   it should be fixed to make it 'portable' or fast or something.
*/

#define caseless_eq(a,b) \
  (((a)!='*') && ((b)!='*') && \
   (((a)==(b)) || \
    ((a)=='?') || \
    ((b)=='?') || \
    (isalpha(a) && isalpha(b) && \
     ((toupper(a) == (b)) || (toupper(b) == (a))))))
/* 
  First major change here: Addition of wildcarding. Specifically, add
  '?' [easily done at the character level] and '*' [only terminal, for 
  now.]
*/
ms_ret match_strands(s1,s2)
     char *s1, *s2;
{
  while (caseless_eq(*s1,*s2))		/* while this == that */
    {
      if (*s1 == '\0')	/* if this is zero, bumping it just in case*/
	{
	  return(MS_SUCCESS);
	}
      else
	{
	  s1++;
	  s2++;
	}
    }				/* well, got a mismatch */
  if (((*s1) == '*') || ((*s2) == '*'))
    {
      return(MS_WILD);		/* wildcards, had to win... */
    }
  /* recall of course that '*' is also trapped above as SEP_STAR... */
  if (*s1 == 0)			/* did the first one finish? */
    {
      return(MS_FIRST);
    }
  if (*s2 == 0)			/* did the second one finish? */
    {
      return(MS_SECOND);
    }
  return(MS_FAIL);		/* oh well, they crashed. */
}

void rop_debug();

#define FAILURE TRUE
#define SUCCESS FALSE
#define MAXROPESTACK 32

#ifdef OLDROPCMP
int ropcmp(r1,r2)
     rope *r1, *r2;
{
  register char *rr1, *rr2;
  int ro1=0, ro2=0;
  int rop_level1 = -1;
  int rop_level2 = -1;
  rope *rope_stack1[MAXROPESTACK];
  rope *rope_stack2[MAXROPESTACK];

  dbg_printf("Rope 1:\n"); rop_debug(r1,0);
  dbg_printf("Rope 2:\n"); rop_debug(r2,0);


  for(;;)			/* exit conditions buried... */
    {
      ms_ret ms;

      dbg_printf("rc:typ=%d\n",r1->typ);
      /* pop levels if needed... */
      if (r1->typ == END)
	{
	  if(rop_level1 >= 0)
	    {
	      r1 = rope_stack1[rop_level1--];
	      continue;
	    }
	  else
	    {
	      break;		/* ONLY WAY OUT! */
	    }
	}
      if (r2->typ == END)
	{
	  if(rop_level2 >= 0)
	    {
	      r2 = rope_stack2[rop_level2--];
	      continue;
	    }
	  else
	    {
	      break;		/* ONLY WAY OUT! */
	    }
	}

      /* jump along NEXTs until they vanish */
      if (r1->typ == NEXT)
	{
	  r1 = r1->ind.next;
	  ro1 = 0;
	  continue;
	}
      if (r2->typ == NEXT)
	{
	  r2 = r2->ind.next;
	  ro2 = 0;
	  continue;
	}
#define val(a) (*raw_table[a])
      /* Ignore FORM data in comparisons. */
      if (r1->typ == RAW)
	{
	  if(val(r1->ind.ex) == '*')
	    {
	      return(SUCCESS);	/* Wildcards */
	    }
#ifdef old_raw
	  r1++; ro1 = 0;
	  continue;
#endif
	}
      if (r2->typ == RAW)
	{
	  if(val(r2->ind.ex) == '*')
	    {
	      return(SUCCESS);	/* Wildcards */
	    }
#ifdef old_raw
	  r2++; ro2 = 0;
	  continue;
#endif
	}

      /* Compare SYNCs for multi-rope matches */
      if (r1->typ == SYNC)
	{
	  if (r1->typ == r2->typ)
	    {
	      r1++; ro1 = 0;
	      r2++; ro2 = 0;
	      continue;
	    }
	  else
	    {
	      return(FAILURE);
	    }
	}
      if (r1->typ == VAR)
	{
#ifdef old_raw
	  if(rop_level1 + 1 > MAXROPESTACK)
	    {
	      printf("ropcmp 1 too deep, assuming failure at level %d\n",
		     rop_level1);
	      return(FAILURE);
	    }
	  rope_stack1[++rop_level1] = r1+1; /* where to return to */
	  r1 = ropes_var[r1->ind.ex]; /* where to go next */
	  dbg_printf("ropcmp nesting r1 (%d)\n", rop_level1);
#else
	  r1++; ro1 = 0;
#endif
	  continue;
	}
      if (r2->typ == VAR)
	{
#ifdef old_raw
	  if(rop_level2 + 1 > MAXROPESTACK)
	    {
	      printf("ropcmp 2 too deep, assuming failure at level %d\n",
		     rop_level2);
	      return(FAILURE);
	    }
	  rope_stack2[++rop_level2] = r2+1; /* where to return to */
	  r2 = ropes_var[r2->ind.ex]; /* where to go next */
	  dbg_printf("ropcmp nesting r2 (%d)\n", rop_level2);
#else
	  r2++; ro2 = 0;
#endif
	  continue;
	}

      /* NEXT, END, and SYNC are the only special types. The remainder */
      /* are strands. */

      rr1 = get_strand(r1, ro1);
      rr2 = get_strand(r2, ro2);

      ms = match_strands(rr1,rr2);

      dbg_printf("rcmp:strands (%s)(%s) matched as <%d>\n",rr1,rr2, ms);

      switch(ms)
	{
	case MS_FAIL:
	  return(FAILURE);
	  break;
	case MS_SUCCESS:
	  r1++; ro1 = 0;
	  r2++; ro2 = 0;
	  continue;
	case MS_FIRST:
	  ro2 = strlen(rr1);
	  r1++; ro1 = 0;
	  continue;
	case MS_SECOND:
	  ro1 = strlen(rr2);
	  r2++; ro2 = 0;
	  continue;
	case MS_WILD:
	  /* We KNOW it must all match from here in, so... */
	  return(SUCCESS);
	default:
	  printf("zwgc:ropes Weird return from match_strands:%d\n",ms);
	  abort();
	}
    }
  if ( r1->typ == r2->typ )	/* ...at least one of which is END */
    {
      return(SUCCESS);
    }
  else
    {
      return(FAILURE);
    }
}
#endif

#ifdef YANROPCMP
int ropcmp(r1,r2)
     rope *r1, *r2;
{
  register char *rr1, *rr2;
  int ro1=0, ro2=0;

  dbg_printf("Rope 1:\n"); rop_debug(r1,0);
  dbg_printf("Rope 2:\n"); rop_debug(r2,0);


  for(;;)			/* exit conditions buried... */
    {
      ms_ret ms;

      dbg_printf("rc:typ=%d\n",r1->typ);
      /* pop levels if needed... */
      if (r1->typ == END)
	{
	  break;		/* ONLY WAY OUT! */
	}
      if (r2->typ == END)
	{
	  break;		/* ONLY WAY OUT! */
	}

      /* jump along NEXTs until they vanish */
      if (r1->typ == NEXT)
	{
	  r1 = r1->ind.next;
	  ro1 = 0;
	  continue;
	}
      if (r2->typ == NEXT)
	{
	  r2 = r2->ind.next;
	  ro2 = 0;
	  continue;
	}
#define val(a) (*raw_table[a])
      /* Ignore FORM data in comparisons. */
      if (r1->typ == RAW)
	{
	  if(val(r1->ind.ex) == '*')
	    {
	      return(SUCCESS);	/* Wildcards */
	    }
	}
      if (r2->typ == RAW)
	{
	  if(val(r2->ind.ex) == '*')
	    {
	      return(SUCCESS);	/* Wildcards */
	    }
	}

      /* Compare SYNCs for multi-rope matches */
      if (r1->typ == SYNC)
	{
	  if (r1->typ == r2->typ)
	    {
	      r1++; ro1 = 0;
	      r2++; ro2 = 0;
	      continue;
	    }
	  else
	    {
	      return(FAILURE);
	    }
	}
      if (r1->typ == VAR)
	{

	}
      if (r2->typ == VAR)
	{

	}

      /* NEXT, END, and SYNC are the only special types. The remainder */
      /* are strands. */

      rr1 = get_strand(r1, ro1);
      rr2 = get_strand(r2, ro2);

      ms = match_strands(rr1,rr2);

      dbg_printf("rcmp:strands (%s)(%s) matched as <%d>\n",rr1,rr2, ms);

      switch(ms)
	{
	case MS_FAIL:
	  return(FAILURE);
	  break;
	case MS_SUCCESS:
	  r1++; ro1 = 0;
	  r2++; ro2 = 0;
	  continue;
	case MS_FIRST:
	  ro2 += strlen(rr1);
	  r1++; ro1 = 0;
	  continue;
	case MS_SECOND:
	  ro1 += strlen(rr2);
	  r2++; ro2 = 0;
	  continue;
	case MS_WILD:
	  /* We KNOW it must all match from here in, so... */
	  return(SUCCESS);
	default:
	  printf("zwgc:ropes Weird return from match_strands:%d\n",ms);
	  abort();
	}
    }
  if ( r1->typ == r2->typ )	/* ...at least one of which is END */
    {
      return(SUCCESS);
    }
  else
    {
      return(FAILURE);
    }
}
#endif /* YANROPCMP */

char *rop_prt();

int strwildcasecmp(s1, s2)
     char *s1, *s2;
{
  int ln1 = max(strlen(s1)-1,0), ln2 = max(strlen(s2)-1,0), nmax;

  if(s1[ln1] == WILDCHAR)
    {
      nmax = ln1;
    }
  else if(s2[ln2] == WILDCHAR)
    {
      nmax = ln2;
    }
  else
    {
      nmax = max(ln1+1, ln2+1);
    }
  return(strncasecmp(s1, s2, nmax));
}

int ropcmp(r1,r2)
     rope *r1, *r2;
{
  char *zr1 = rop_prt(r1);
  char *zr2 = rop_prt(r2);

  if(strwildcasecmp(zr1, zr2))
    {
      free(zr1);
      free(zr2);
      return FAILURE;
    }
  else
    {
      free(zr1);
      free(zr2);
      return SUCCESS;
    }
}


rope *ropcat(r1, r2)
     rope *r1, *r2;
{
  register rope *rs = r1;

  while( rs->typ != END )	/* scan for the end of rope... */
    {
      if ( rs->typ == NEXT )	/* don't stumble on NEXTs... */
	{
	  rs = rs->ind.next;
	}
      else
	{
	  rs++;
	}
    }
  rs->typ = NEXT;		/* set link to r2 */
  rs->ind.next = r2;

  return(r1);
}

void ropappend(r, re, size)
     rope *r;
     rope re;
     int size;
{
  register rope *rs = r;

  while( rs->typ != END )	/* scan for end of rope... */
    {
      if ( rs->typ == NEXT )	/* don't stumble on NEXTs... */
	{
	  rs = rs->ind.next;
	  r = rs;		/* we only need the size on the last one */
	}
      else
	{
	  rs++;
	}
    }

  if ( (rs - r + 1) < size )	/* we have at least one left, bump the end */
    {
      *rs = re;
      rs++;
      rs->typ = END;
    }
  else				/* END in final slot, make a NEXT and jump */
    {
      rs->typ = NEXT;
      rs->ind.next = ropinit(size);
      rs = rs->ind.next;
      *rs = re;
      rs++;
      rs->typ = END;
    }
}

rope *ropinit(size)
     int size;
{
  rope *rs;

  /* figure out whether to malloc or not...or use a freelist? :-) */
  rs = (rope *)malloc(size * sizeof(rope));
  rs->typ = END;
  return(rs);
}

void ropfree(r)
     rope *r;
{
  rope *rs = r;

  while(r->typ != END)
    {
      if (r->typ == NEXT)
	{
	  r = r->ind.next;
	  free(rs);
	  rs = r;
	}
      else if (r->typ == ROPE)
	{
	  ropfree(r->ind.rope);
	  r++;
	}
      else
	{
	  relfree(*r);
	  r++;
	}
    }
  free(rs);
}

void relfree(re)
     rope re;
{
  switch(re.typ)
    {
    case VAR:
      break;
    case FIELD:
      break;
    case HEAD:
      break;
    case RAW:
      break;
    case NEXT:
      printf("zwgc:ropes Oops, relfree on an un-free rope\n");
      break;
    case SYNC:
      break;
    case FORM:
      break;
    case SHOW:
      break;
    case COMMAND:
      break;
    case END:
      printf("zwgc:ropes relfree on end of rope\n");
      abort();
    case ROPE:
      printf("zwgc:ropes relfree on nested rope\n");
      abort();
    default:
      printf("zwgc:ropes Enumeration failure?(%d)\n",re.typ);
      abort();
    }

}
void rop_debug(r,l)
     rope *r;
     int l;
{
  int n;

  for(;;)
    {
      for(n = l; n; n--) dbg_printf("    ");
      if(!r)
	{
	  dbg_printf("NULL rope!\n");
	  return;
	}
      switch(r->typ)
	{
	case VAR:
	  dbg_printf("VAR[%d]:<%s>\n",r->ind.ex, variables[r->ind.ex]);
	  /* try to print the value... */
	  if(!varropes[r->ind.ex]) break;
	  for(n = l; n; n--) dbg_printf("    ");
	  dbg_printf("VALUE VAR[%d]:[\n", r->ind.ex);
	  rop_debug(varropes[r->ind.ex],l+1);
	  for(n = l; n; n--) dbg_printf("    ");
	  dbg_printf("]\n");
	  break;
	case SHOW:
	  dbg_printf("SHOW[%d]:<%s>\n",r->ind.ex, table_shows[r->ind.ex]);
	  break;
	case ROPE:
	  dbg_printf("ROPE[\n");
	  rop_debug(r->ind.rope,l+1);
	  for(n = l; n; n--) dbg_printf("    ");
	  dbg_printf("]\n");
	  break;
	case FIELD:
	  dbg_printf("FIELD[%d]\n",r->ind.ex);
	  break;
	case HEAD:
	  dbg_printf("HEAD[%d]\n",r->ind.ex);
	  break;
	case RAW:
	  dbg_printf("RAW[%d]:<%s>\n",r->ind.ex, raw_table[r->ind.ex]);
	  break;
	case NEXT:
	  dbg_printf("NEXT[0x%x]\n",r->ind.next);
	  r = r->ind.next;
	  continue;		/* skip later r++ */
	  break;
	case SYNC:
	  dbg_printf("SYNC[]\n");
	  break;
	case FORM:
	  dbg_printf("FORM[%d]\n",r->ind.ex);
	  break;
	case END:
	  dbg_printf("END.\n");
	  return;
	  break;
	case COMMAND:
	  dbg_printf("COMMAND[%s]\n", table_commands[r->ind.ex]);
	  break;
	default:
	  printf("zwgc:ropes Enumeration failure?(%d)\n",r->typ);
	  abort();
	} 
      r++;
    }
}
/* Compare this with rop_debug? */
char *realloc();

char *rop_prt(r)
     rope *r;
{
  int sx, sxin;
  char *x, *xin;

  x = malloc(1);
  x[0] = '\0';
  sx = 0;
  if(!r)
    {
      return(x);
    }
  for(;;)
    {
      switch(r->typ)
	{
	case ROPE:
	  dbg_printf("ROPE[0x%x]:\n",r->ind.next);
	  xin = rop_prt(r->ind.next);
	  sxin = strlen(xin);
	  x = realloc(x, sx + sxin + 1);
	  bcopy(xin, x + sx, sxin + 1);
	  sx += sxin;
	  free(xin);
	  break;
	case VAR:
	  dbg_printf("VAR[%d]:\n",r->ind.ex);
	  xin = rop_prt(varropes[r->ind.ex]);
	  sxin = strlen(xin);
	  x = realloc(x, sx + sxin + 1);
	  bcopy(xin, x + sx, sxin + 1);
	  sx += sxin;
	  free(xin);
	  break;
	case FIELD:
	case HEAD:
	case RAW:
	  dbg_printf("FIELD[%d]\n",r->ind.ex);
	  xin = get_strand(r,0);
	  sxin = strlen(xin);
	  x = realloc(x, sx + sxin + 1);
	  bcopy(xin, x + sx, sxin + 1);
	  sx += sxin;
	  break;
	case NEXT:
	  dbg_printf("NEXT[0x%x]\n",r->ind.next);
	  r = r->ind.next;
	  continue;		/* skip later r++ */
	  break;
	case SYNC:
	  dbg_printf("SYNC[]\n");
	  break;
	case FORM:
	  dbg_printf("FORM[%d]\n",r->ind.ex);
	  xin = " ";
	  sxin = strlen(xin);
	  x = realloc(x, sx + sxin + 1);
	  bcopy(xin, x + sx, sxin + 1);
	  sx += sxin;
	  break;
	case SHOW:
	  dbg_printf("Show command[]\n");
	  /* @bold, &c. */
	  break;
	case COMMAND:
	  dbg_printf("COMMAND ...\n");
	  /* main commands, shouldn't be there... */
	  break;
	case END:
	  dbg_printf("END.\n");
	  return(x);
	  break;
	default:
	  printf("zwgc:ropes Enumeration failure?(%d)\n",r->typ);
	  abort();
	} 
      r++;
    }
}
