# include	<stdio.h>
# include	<ingres.h>
# include	<aux.h>
# include	<symbol.h>
# include	<tree.h>
# include	"../decomp/globs.h"
# include	<sccs.h>
# include	<errors.h>

char		*need();

SCCSID(@(#)string.c	8.5	5/30/88)

/*
**	This file contains the string
**	manipulation routines
*/







/*
**	Concat takes the two character strings in
**	s1 and s2 and concatenates them together
**	into a new location.
**
**	trailing blanks are removed from the first symbol.
**	The size of the concatenation equals the sum of
**	the two original strings.
*/

concatsym(s1, s2)
register SYMBOL	*s1, *s2;
{
	register char	*p;
	int		size1, size2, i;
	char		*px;

/*	size1 = size(s1);*/	/* compute size w/o trailing blanks */
	size1 = s1->len & I1MASK;
	if (size1 == 0 && s1->len != 0)
		size1++;	/* don't allow string to be trunc to zero length */
	size2 = s2->len & I1MASK;	/* size of second string remains the same */
	i = (s1->len & I1MASK) + size2;	/* i equals sum of original sizes */
	if (i > MAXFIELD)
		i = MAXFIELD;	/* a string can't exceed this size */
	if (size2 + size1 > MAXFIELD)
		size2 = MAXFIELD - size1;	/* adjust size2 to not exceed MAXFIELD */

	px = p = need(De.ov_ovqpbuf, i);	/* request the needed space */
	bmove(s1->value.sym_data.cptype, p, size1);	/* copy first string */
	p = &p[size1];
	bmove(s2->value.sym_data.cptype, p, size2);
	p = &p[size2];
	s1->value.sym_data.cptype = px;
	s1->len = i;
	/* pad with blanks if necessary */
	i -= size1 - size2;
	while (i--)
		*p++ = ' ';

#	ifdef xOTR1
	if (tTf(82, 3))
	{
		printf("Concat:");
		prstack(s1);
	}
#	endif
}
/*
**	Size determines the size of a character symbol
**	without trailing blanks.
*/

size(s)
register SYMBOL	*s;
{
	register char		*c;
	register int		i;

	c = s->value.sym_data.cptype;
	i = s->len & I1MASK;

	for (c += i; i; i--)
		if(*--c != ' ')
			break;

	return (i);
}
/*
**	Converts the numeric symbol to
**	ascii. Formats to be used are determined
**	by Out_arg.
*/

ascii(s)
register SYMBOL	*s;
{
	register int		i;
	register char		*p;
	char			temp[MAXFIELD];
	double			dtemp;
	extern struct out_arg	Out_arg;	/* used for float conversions */
	char			*locv();

	p = temp;
	switch(s->type)
	{

	  case INT:
		if (s->len == 4)
		{
			i = Out_arg.i4width;
			p = locv(s->value.sym_data.i4type);
		}
		else
		{
			if (s->len == 2)
			{
				itoa(s->value.sym_data.i2type, p);
				i = Out_arg.i2width;
			}
			else
			{
				itoa(s->value.sym_data.i1type, p);
				i = Out_arg.i1width;
			}
		}
		break;

	  case CHAR:
		return;

	  case FLOAT:
		if (s->len == 4)
		{
			i = Out_arg.f4width;
			dtemp = s->value.sym_data.f4type;
			ftoa(dtemp, p, i, Out_arg.f4prec, Out_arg.f4style);
		}
		else
		{
			i = Out_arg.f8width;
			ftoa(s->value.sym_data.f8type, p, i, Out_arg.f8prec, Out_arg.f8style);
		}
	}
	s->value.sym_data.cptype = need(De.ov_ovqpbuf, i);
	pmove(p, s->value.sym_data.cptype, i, ' ');	/* blank pad to fixed length i */
	s->type = CHAR;
	s->len = i;
}
/*
**	LEXCOMP performs character comparisons between the two
**	strings ss1 and ss2. All blanks and null are ignored in
**	both strings. In addition pattern matching is performed
**	using the "shell syntax". Pattern matching characters
**	are converted to the pattern matching symbols PAT_ANY etc.
**	by the scanner.
**
**	Pattern matching characters can appear in either or
**	both strings. Since they cannot be stored in relations,
**	pattern matching chars in both strings can only happen
**	if the user types in such an expression.
**
**	examples:
**
**	"Smith, Homer" = "Smith,Homer"
**
**	"abcd" < "abcdd"
**
**	"abcd" = "aPAT_ANYd"
**
**	returns	<0 if s1 < s2
**		 0 if s1 = s2
**		>0 if s1 > s2
*/

char	*S1,*S2;
int	L1,L2;

lexcomp(s1, l1, s2, l2, x)
register char	*s1, *s2;
register int	l1, l2;
int		x;
{
	char		c1, c2;
	int		howmany = Patnum;	/* howmany PAT_SPEC char matchings so far */
	int 		retval;
	int		i;
	char		*t1, *t2;

#	ifdef xOTR1
	if (tTf(82, 4))
	{
		printf("LEXCOMP: starting...\n");
		t1 = s1;
		t2 = s2;
		printf("howmany = %d\n", howmany);
		printf("first string= '");
		for (i = 0; i < l1; i++)
			printf("%c", *t1++);
		printf("'\n");
		printf("length = %d\n", l1);
		printf("second string= '");
		for (i = 0; i < l2; i++)
			printf("%c", *t2++);
		printf("'\n");
		printf("length = %d\n", l2);
	}
#	endif
	/* save initial information in case a PAT_GLOB is found */
	if (x==0) 
	{
	    S1 = s1; 
	    S2 = s2;
	    L1 = l1; 
	    L2 = l2;
	}
loop:
	while (l1--)
	{
		switch (c1 = *s1++)
		{

		  /* case ' ': */
		  case '\0':
			break;

		  case PAT_GLOB:
		  {
			return(gmatch(S1,L1,S2,L2));
	       	  }

		  case PAT_ANY:
			return (pmatch(FALSE, s1, l1, s2, l2));

		  case PAT_SPEC:
			retval = pmatch(*(s1-1),++s1, --l1, s2, l2);

			/*
			** If there was no match in pmatch, 
			** reset Patnum to previous value
			*/

#	ifdef xOTR1
		if (tTf(82,4))
			printf("lexcomp: return %d\n", retval);

#	endif
			if (retval) 
			   Patnum = howmany;
			return (retval);

		  case PAT_LBRAC:
			return (lmatch(s1, l1, s2, l2));

		  default:
			while (l2--)
			{
				switch (c2 = *s2++)
				{

				  /* case ' ': */
				  case '\0':
					continue;

				  case PAT_GLOB:
				  {
					return(gmatch(S2,L2,S1,L1));
				  }

				  case PAT_ANY:
					return( pmatch(FALSE,s2,l2,--s1,++l1));

				  case PAT_SPEC:
					retval = pmatch(*(s2-1),++s2, --l2, --s1, ++l1);

#	ifdef xOTR1
					if (tTf(82,4))
						printf("lexcomp: retval = %d\n", retval);
# 	endif

					if (retval) 
					   Patnum = howmany;
					return (retval);

				  case PAT_LBRAC:
					return (lmatch(s2, l2, --s1, ++l1));

				  default:
					if (c1 == c2)
						goto loop;
					if (c1 == PAT_ONE || c2 == PAT_ONE)
						goto loop;
#	ifdef xOTR1
					if (tTf(82,4))
						printf("lexcomp: 2.return %d\n",c1 - c2);
#	 endif
					return (c1 - c2);
				}
			}
#	ifdef xOTR1
			if (tTf(82,4))
				printf("lexcomp: returning 1\n");
#	endif
			return (1);	/* s1 > s2 */
		}
	}

	/* examine remainder of s2 for any characters */
	while (l2) {
		l2--;
		if ((c1 = *s2++) == PAT_SPEC) 
		{
		    pat_insert("",0,*s2,0);  /* insert empty string */
		    *s2++,l2--;	        /* get rid of index */
		}
		/* if (((c1 = *s2) != ' ') && (c1 != '\0') */
		if ((c1 != ' ') && (c1 != '\0') 
			&& (c1 != PAT_ANY) && (c1 != PAT_SPEC))
		{
#	ifdef xOTR1
			if (tTf(82,4))
				printf("lexcomp: returning -1\n");
#	endif
			Patnum = howmany;
			return (-1);	/* s1 < s2 */
		}
	}
#	ifdef xOTR1
		if (tTf(82,4))
			printf("lexcomp: returning 0\n");
#	endif
	return (0);
}


/*
**	PMATCH
**
**	Checks if a pattern containing a pattern matching character 
**	(either PAT_ANY or PAT_SPEC) is found in a string.
**
**	Returns:
**		 0 -- if pattern found in string
**		-1 -- if no match
**
**	Called By:
**		lexcomp
**
**	Calls:
**		pat_insert, lexcomp
*/

pmatch(patarg,pat, plen, str, slength)
char 	patarg;		/* index for pattern matching--FALSE when no indices 
				used */
char	*pat;		/* the string holding the pattern matching char */
char	*str;		/* the string to be checked */
int	plen, slength;	/* the lengths */
{
	register char	d, *s;
	register int	slen,count;
	char		c;

#	ifdef xOTR1
		if (tTf(82,5))
		{
			printf("PMATCH: starting\n");
			printf("patarg = %c\n",patarg);
			printf("string with pattern char = %s\n", pat);
			printf("string len = %d \n", plen);
			printf("string to check = %s\n", str);
			printf("string len = %d\n", slength);
		}
#	endif
	s = str;
	slen = slength;

	if (plen == 0) 
	{
		if ( patarg )
		{
			pat_insert(str,slength,patarg,1);
		}
		return  (0);	/* a match if no more chars in p */
	}

	/*
	** If the next character in "pat" is not another
	** pattern matching character, then scan until
	** first matching char and continue comparison.
	*/
	if ((c = *pat) != PAT_ANY && c != PAT_SPEC 
		&& c != PAT_LBRAC && c != PAT_ONE)
	{
		count = 0;
		while (slen--)
		{
			if ((d = *s) == c || d == PAT_ANY || d == PAT_SPEC 
				|| d == PAT_LBRAC && d != PAT_ONE)
			{
				if ( patarg )
				{
					pat_insert(str,count,patarg,0);
				}
				if (lexcomp(s, slen + 1,pat, plen, 1) == 0)
				{
					return (0);
				}
			}
			s++;
			count++;
		}
	}
	else
	{
		while (slen)
		{
			if (lexcomp(s++, slen--,pat, plen, 1) == 0)
				return (0);	/* match */
		}
	}
	return (-1);	/* no match */
}


lmatch(pat, plen, str, slen)
char	*pat;	/* the string holding the pattern matching char */
char	*str;	/* the other string */
int	plen, slen;	/* their respective sizes */
{
	register char	*p, *s;
	register int	cc;
	int		oldc, c, found;

#	ifdef xOTR1
		if (tTf(82,6))
		{
			printf("LMATCH: starting...\n");
			printf("Pat = %s, length = %d\n", pat, plen);
			printf("Str = %s, length = %d\n", str, slen);
		}
#	endif
	p = pat;
	s = str;

	/* find a non-blank, non-null char in s */
	while (slen--)
	{
		if ((c = *s++) != ' ' && c != '\0')
		{
			/* search for a match on 'c' */
			found = 0;	/* assume failure */
			oldc = 0777;	/* make previous char large */

			while (plen--)
			{

				switch(cc = *p++)
				{

				  case PAT_RBRAC:
					if (found)
					{
						return (lexcomp(s, slen,p, plen, 1));
					}
					return (-1);

				  case '-':
					if (plen-- == 0)
						return (-1);	/* not found */
					if (oldc <= c && c <= (cc = *p++))
						found++;
					break;

				  default:
					if (c == (oldc = cc))
						found++;
				}
			}
			return (-1);	/* no match */
		}
	}
	return (1);
}



/*
**	GMATCH: checks for string matches while grabbing all instances
**		of the string delimited by PAT_GLOB.
**
*/

gmatch(s1,l1,s2,l2)
register char 	*s1,*s2;
register int	l1,l2;
{
	char	*start,*end,*pat,*c,*temps2;
	int	slen=0,elen=0,plen=0;
	int	index,stindex,endex;
	int	retval,templ2,smlen,first;
	GLIST	*g;

#	ifdef xOTR1
		if (tTf(82,7))
		{
			printf("GMATCH: s1 = %s\n", s1);
			printf("GMATCH: l1 = %d\n", l1);
			printf("GMATCH: s2= %s\n", s2);
			printf("GMATCH: l2 = %d\n", l2);
		}
#	endif
	c = s2;
	for (c += l2; l2; l2--)
		if(*--c != ' ')
			break;
	c = s1;
	for (c += l1; l1; l1--)
		if(*--c != ' ')
			break;

	if (*s1 == PAT_SPEC)
	{
	    s1 += 2;
	    l1 -= 2;
	}
	else if (*s1 == PAT_ANY)
	{
	    s1++;
	    l1--;
	}
	c = (start = need(De.ov_ovqpbuf, l1));
	while (l1-- && PAT_GLOB != *s1++) {
	    *c++ = *(s1-1);
	    slen++;
	}
	c = (pat = need(De.ov_ovqpbuf, l1));
	while ( l1-- && *s1++ != PAT_GLOB) {
	    *c++ = *(s1-1);
	    plen++;
	}
	end = s1;
	elen = l1;

	if (slen != elen && (!slen || !elen))
	{
	    return(-1);
	}

	Globs = NULL;
	if (!slen) 
	{
	    index = scanstr(s2,l2,pat,plen,1,1);
	    if (index == -1) 
	    {
		return(-1);
	    }
	    add_glob(s2,index);
	    for (;;) {			/* this loop ends when index is -1 */
		s2 += index + plen;
		l2 -= index + plen;
		index = scanstr(s2, l2,pat,plen,1,1);
		if (index == -1)
		{	/* since string is finite, guaranteed to happen */
		    add_glob(s2,l2);
		    Globfoot->next = NULL;
		    return(0);
		}
		add_glob(s2,index);
	    }
	}
	else {
	    retval = 1;
	    first = 0;
	    temps2 = s2;
	    templ2 = 0;
	    for(;;) {
		if (first) {
		    s2 += smlen + elen;
		    l2 -= smlen + elen;
		    templ2 += smlen + elen;
		}
		else
		    first = 1;
		if ((stindex=scanstr(s2,l2,start,slen,1,1)) == -1 ||
		    (endex = scanstr(s2+stindex+slen,l2-stindex-slen,end,elen,1,1)) == -1)
		    {
			if (!retval) 
			{
			    templ2 += l2;
			    add_glob(temps2,templ2);
			}
			return(retval);
		    }
		s2 += stindex + slen;
		l2 -= stindex + slen;
		templ2 += stindex + slen;
		smlen = endex;
		for (;(index = scanstr(s2,smlen,pat,plen,1,1)) != -1;)
		{
		    retval = 0;
		    templ2 += index;
		    add_glob(temps2,templ2);
		    temps2 += templ2 + plen;
		    templ2 = 0;
		    s2 += index + plen;
		    l2 -= index + plen;
		    smlen -= index + plen;
		}
	    }
	}
		    
}


add_glob(str,slen)
char	*str;
int	slen;
{
  char  *malloc();
#	ifdef xOTR1
		if (tTf(82,8))
			printf("ADD_GLOB: str = %s, slen = %d\n", str, slen);
#	endif
	    if (Globs == NULL) {
		Globs =
		  (Globfoot = (GLIST *) malloc(sizeof(GLIST)));
		Globs->string = malloc(slen);
		bmove(str,Globs->string,slen);
		Globlen = Globs->len = slen;
		Globnum = 1;
	    }
	    else {
		Globfoot->next = (GLIST *) malloc(sizeof(GLIST));
		Globfoot = Globfoot->next;
		Globfoot->string = malloc(slen);
		bmove(str,Globfoot->string,slen);
		Globlen += (Globfoot->len = slen);
		Globnum++;
	    }

}

/*
**	PAT_INSERT 
**	
**	Moves str and its corresponding length into Pats[index] 
**	where index refers to the PAT_SPEC index.
**
**	May be called even though the Pats[index] string is not the one 
**	which will eventually be used for the replace.  For instance, 
**	if the pattern matching coincides but the line number doesn't.
**
**	Side Effects:
**		Patnum is incremented indicating an insertion was done.
**		Pats[index] record gets a new string and length.
**
**	Returns:  none
**
**	Calls:	bmove
**
**	Called By:
**		pmatch, lexcomp
*/

pat_insert(str,slen,where,no_blanks)
char 	*str;		/* the string being moved to Pats[where] */
int 	slen;		/* length of str */
char 	where;		/* index into Pats */
int	no_blanks;
{
	int	index;		/* integer value of Pats' index */
	int 	i;
	char	*malloc();

	index = where - '0';
	if (no_blanks)		/* get rid of blanks */
	    while (*(str + slen - 1) == ' ')  
		slen--;

	if (Pats[index].string) 		/* for overwriting string */
	{
		free(Pats[index].string);
		Pats[index].string = NULL;	/* Not really necessary, but helps with debugging */
	}
		Patnum++;

	Pats[index].string = malloc(slen);
	bmove(str,Pats[index].string,slen);	/* move str to Pats[].string */
	Pats[index].len = slen;
#	ifdef xOTR1
		if (tTf(82,9))
		{
			for (i = 0; i < PATNUM; i++)
				printf("Pats[%d] = %s, len = %d\n", i,Pats[i].string, Pats[i].len);
		}
#	endif

}


/*
**
**	INSERT_CHARS replaces all [PAT_SPEC, index] pairs with strings from 
**	the Pats[] array.  The PAT_SPEC index corresponds to the index into
**	the Pats[] array.
**
**	Calls:	bmove
**
**	Called by:  interpret
**
**	Returns:  none	
*/

insert_chars(op)
SYMBOL *op;
{
    char 	*st, *s,	/* pointers to SYMBOL string */
		*new;		/* pointer to new string being formed */
    int 	l,		/* length of SYMBOL string */
		size = 0;	/* size of new string being formed */
    int		tot,		/* total size of new string being formed */
		index,		/* PAT_SPEC index */
		flag=0;

#	ifdef xOTR1
		if (tTf(82,10))
			printf("INSERT_CHARS: starting...\n");
#	endif
    l = op->len & I1MASK;
    st = s = op->value.sym_data.cptype; 
    while (*(s+l-1) == ' ')
	l--;				/* don't worry about blanks */
    tot = l;
    while (l--) {
	if (*st == PAT_GLOB)
	{
		insert_glob(&s,++st,&tot,l);
		break;
	}
	if (*st++ == PAT_SPEC) {
		index = *st++ - '0';
		l--;

		/* subtract 2 for PAT_SPEC and corresponding index */
		tot += Pats[index].len - 2;

		new = need(De.ov_ovqpbuf, tot);
		if (size)
		    bmove(s,new,size);

		/* append the Pats[] string to the currently forming string */
		bmove(Pats[index].string,new+size,Pats[index].len);

		if (!flag)
		    flag = 1;
		else
		    free(s);
		s = new;  
		size += Pats[index].len;
		if (l) {
		    bmove(st,new+size,l);
		    st = new + size;
		}	
	}
	else 
	    size++;
    } /* while */

    /*
    **  replace SYMBOL string with
    **  new string and length
    */
    op->value.sym_data.cptype = s;
    op->len = tot;
}


insert_glob(start,rest,slen,rlen)
char	**start,*rest;
int	*slen,rlen;
{ 
	char	*pat = rest,*new;
	int	plen = 0,newlen,i;
	GLIST	*g;	

	while (rlen-- && *rest++ != PAT_GLOB)
	    plen++;
	/* put in error checking about 2nd PAT_GLOB */
	*slen -= plen + 2 + rlen;
	newlen = *slen + rlen + Globlen + (Globnum-1)*plen;
	new = need(De.ov_ovqpbuf, newlen);
	bmove(*start,new,*slen);
	*start = new;
	new += *slen;
	for (i = Globnum,g=Globs;i>1;i--,g=g->next) {
		bmove(g->string,new,g->len);
		new += g->len;
		bmove(pat,new,plen);
		new += plen;
	}
	bmove(g->string,new,g->len);
	new += g->len;
	bmove(rest,new,rlen);
	*slen = newlen;
}



int flink[MAXFIELD];  		/* array for storing failure points in string */

newstring(op1,op2)
register SYMBOL	*op1,*op2;
{
	int	stsize,psize,index,index2;

	psize = op2->len & I1MASK;	/* ignore trailing blanks */
	stsize = op1->len & I1MASK;
	if (op2->start != -1)
	{
		index = op2->start;
	}
	else
		index = scanstr(op1->value.sym_data.cptype,stsize,
				op2->value.sym_data.cptype,psize,
				CLOSED,(char) 1);	/* get start of string */	
	if (index != -1) {
		index2 = index + psize;
		bmove(op1->value.sym_data.cptype + index2,
		      op1->value.sym_data.cptype + index, stsize - index2);
		for (index += stsize - index2; index < stsize; index++)
		      *(op1->value.sym_data.cptype + index) = ' ';
	}
}

	    

createlink(pat,plen)
char	*pat;
int	plen;
{
    int i,j;

    flink[0] = -1;
    i = 1;
    while (i < plen) 
    {
	j = flink[i-1];
	while (j != -1 && pat[j] != pat[i-1])
	    j = flink[j];
	flink[i] = j + 1;
	i += 1;
    }
}


backlink(pat,plen)
char	*pat;
int	plen;
{
    int i,j;

    flink[plen - 1] = plen;
    i = plen - 2;
    while (i >= 0) 
    {
	j = flink[i+1];
	while (j != plen && pat[j] != pat[i+1])
	    j = flink[j];
	flink[i] = j - 1;
	i -= 1;
    }
}



/*
**	SCANSTR:  Scan a string for a pattern.  
**
**	Returns:
**		-1 -- couldn't find pattern in string
**		index in string to start of pattern -- if getstart is true
**		index in string following pattern   -- if getstart is false
*/

scanstr(str,slen,pat,plen,getstart,num)
char	*str,		/* string being scanned 			     */
	*pat;		/* pattern being searched for 			     */
int	slen,		/* str length					     */
	plen;		/* pat length					     */
int 	getstart;	/* if true, include pat in the string to be returned */
char	num;		/* number of occurance to look for		     */
{
    int i,	/* index into str */
	j,	/* index into pat */
	k,
	found;	/* true when pattern found in string */

# ifdef xOTR1
	if (tTf(82,11))
	{
		printf("SCANSTR: \n");
		printf("str = %s, len = %d\n", str, slen);
		printf("pat = %s, len = %d\n", pat, plen);
	}
# endif

    createlink(pat,plen);
    i = -1;

    /* for each occurance of pattern in string */
    for (k = 0; k < (num & I1MASK); k++) {
	i += 1;
	j = 0;
	found = 0;
	while (i < slen) {

	    /* keep searching str until a potential match for pat is found */
	    while ( j != -1 && pat[j] != str[i])
		j = flink[j];

	    if (j == plen-1) 	/* found pat in str */
	    {
		found = 1;
		break;
	    }
	    else {		/* else check that rest of pat matches */
		i += 1;
		j += 1;
	    }
	}
	if (!found || i == slen) return(-1);	/* didn't find pat in str */
    }

    /** at this point, found pattern in string **/
    if (getstart)
    {
	return(i-plen+1);
    }
    else 
    {
	return(i+1);
    }
} /* scanstr */

/*
**	BACKSCAN
**
**	Searches backwards through string for pattern.
**
**	Returns:
**		-1 -- if pattern not found
**		index in string where pattern starts -- if getstart is true
**		index in string right after pattern ends -- if getstart is false
*/

backscan(str,slen,pat,plen,getstart,num)
char	*str,		/* string being scanned 			*/
	*pat;		/* pattern being searched for 			*/
int	slen,		/* length of string				*/
	plen;		/* length of pattern				*/
int 	getstart;	/* if true, return pointer which includes pat	*/
			/* if false, return pointer following pat	*/
char 	num;		/* which occurance of pat in string		*/
{
    int i,		/* index into string		   */
	j,		/* index into pat and flink	   */
	k,		/* number of occurance found	   */
	found;		/* true if pattern found in string */

# ifdef xOTR1
	if (tTf(82,12))
	{
		printf("BACKSCAN: \n");
		printf("str = %s, len = %d\n", str, slen);
		printf("pat = %s, len = %d\n", pat, plen);
	}
# endif
    backlink(pat,plen);		/* set up flink for backwards scanning */
    i = slen ;

    /* for each occurance of pat in string */
    for (k = 0; k < (num & I1MASK); k++) {
	i -= 1;
	j = plen - 1;
	found = 0;

	/* search for pat from end of string until whole string is examined */
	while (i >= 0) {
	    while ( j != plen && pat[j] != str[i])
		j = flink[j];
	    if (j == 0)  {
		found = 1;
		break;
	    }	
	    else {
		i -= 1;
		j -= 1;
	    }
	}
	if (!found || i < 0) return(-1);
    }
    /* return pointers to pattern in string */
    if (getstart)
    {
	return(i);
    }
    else 
    {
	return(i+plen);
    }
} /* backscan */

getend(len,dropend,howmany)
int	len,dropend,howmany;
{
	int i;

	for (i = 0;i < (howmany & I1MASK); i++)
	    len--;
	if (dropend)
	    len--;
	return(len);
}

/*
**	GRABSTRING grabs a string described by a pattern matching 
**	interval in a query.
**
**	Called by:  getsymbol
**
**	Calls:	scanstr, backscan, getend, specdelim
**
**	Returns:
**		NULL -- if pattern was not found in string
**		ptr to pattern which matches interval -- otherwise
*/

char *
grabstring(strinfo,str,len,startptr)
STRKEEPER	*strinfo;		/* info about delimitors */
char		*str;			/* string to search */
int		*len;			/* length of string */
int		*startptr;
{
	int 	start=0,end=0;		/* start and end of substring */
	int	done = 0;		
	char	*s;
	char 	leftint, rightint;	/* type of interval */
	char 	leftnum, rightnum;	/* number of occurrence to find */
	char 	leftspec, rightspec;	/* special chars 1= special delim */
					/* 2 = search backwards */
	char	*leftpat, *rightpat;	/* left and right patterns */
	int	stsearch;		/* where to start searching 2nd time */
	int	leftlen, rightlen;	/* lengths of patterns returned from specdelim*/

	/* initialization */
	leftint = strinfo->type[0];
	rightint = strinfo->type[1];
	leftnum = strinfo->number[0];
	rightnum = strinfo->number[1];
	leftspec = strinfo->flag[0];
	rightspec = strinfo->flag[1];
	leftpat = strinfo->string[0];
	rightpat = strinfo->string[1];

	*len &= I1MASK;			/* only look at lower byte */

	while (*(str+*len-1) == ' ')	/* find last nonblank char of string */
	    *len -= 1;

# ifdef xOTR1
	if (tTf(82,1))
	{
		printf("GRABSTRING:\n");
		printf("str = %s, len = %d\n", str, *len);
		printf("leftint = %d, leftnum = %d, leftspec = %d\n", leftint, leftnum, leftspec);
		printf("left pattern = %s, len = %d\n", leftpat, strlen(leftpat));
		if (rightpat) {
		  printf("rightint = %d, rightnum = %d, rightspec = %d\n", rightint, rightnum, rightspec);
		  printf("right pattern = %s, len = %d\n", rightpat, strlen(rightpat));
		}
	}
# endif

	/* search for left endpoint */

	/* CASE 1: special chars */
	if (leftspec & 1)
	{
	    	start = specdelim(str,*len,leftpat,leftint,leftnum,&leftlen);
		if (leftint == CLOSED)
			stsearch = start + leftlen;
		else
		{
			start += leftlen;
			stsearch = start;
		}
	}
	/* CASE 2: backwards searching */
	else if (leftspec & 2)
	{
	    	if (leftpat == NULL)
			start = 1 + getend(*len,leftint,leftnum);
	    	else
			start = backscan(str , *len, leftpat, 
					 strlen(leftpat),leftint,leftnum);
		if (leftint == CLOSED)
			stsearch = start + strlen(leftpat);
		else
			stsearch = start;
	}
	/* CASE 3: forwards searching */
	else					
	{
		    start = scanstr(str + start, *len, leftpat, 
				    strlen(leftpat),leftint,leftnum);
		if (leftint == CLOSED)
			stsearch = start + strlen(leftpat);
		else
			stsearch = start;
	}



	if (start == -1)		/* if pattern was not found in str */
	{
		return(NULL);
	}

	/* search for right endpoint */

	/* CASE 1: special chars */
	else if (rightspec & 1)
	{
		if ((end = specdelim(str + stsearch,*len - stsearch,
					  rightpat, 1 - rightint, rightnum,
					  &rightlen)) == -1)
			return(NULL);
		else
		{
			if (rightint == CLOSED)
				end += stsearch + rightlen;
			else
				end += stsearch;
		}
	}
	/* Backwards searching */
	else if (rightspec & 2)
	{
	  if (rightpat == NULL)
	    end = *len;
	  else
	    end = backscan(str, *len, rightpat, strlen(rightpat),1 - rightint,
			   rightnum);
	}
	/* Forwards searching */
	else
	{
	  if (rightpat == NULL)
	    end = stsearch;
	  else
	    if ((end = scanstr(str + stsearch, *len,rightpat, 
			       strlen(rightpat),1 - rightint,
			       rightnum)) == -1)
	      return(NULL);
	    else
	      {
		end += stsearch;
	      }
	}

	
	if (end == -1 || end - start <= 0)	/* if end of interval couldn't 
						** be found or end did not come 
						** after start */
	{
		return(NULL);
	}
	else
	{	
	    *len = end - start;
	    s = need(De.ov_ovqpbuf, *len);
	    bmove (str + start, s, *len);
	    *startptr = start;
	}


	return(s);
} /* grabstring */

/*
**	SPECDELIM -- scan a string for a pattern specified by a special
**		delimiter 
**
**		Parameters:
**			str - string to be scanned
**			slen - length of string
**			dname - name of delimitor
**			getstart - type of interval
**			num - occurrence of pattern to look for
**
**		Returns:
**			index into string of pattern
**			-1 if pattern not found
**			-2 if delimitor was never defined
**
**		Called by:
**			grabstring
**
*/
specdelim(str, slen, dname, getstart, num, plen)
char	*str;
int	slen;
char	*dname;
int	getstart;
char	num;
int	*plen;
{
	extern DELIMLIST	*Delimhead;	/* ptr to queue of delims */
	DELIMLIST		*d;
	DMAP			*map;		/* ptr to bitmap */
	char			patch;
	int			start = -1;	/* index to start of pattern */
	int			match;		/* true while a pattern is matching */
	char			*savestr;
	int			savelen;
	int			k;
	int			i;


# ifdef xOTR1
	if (tTf(82,2))
	{
		printf("SPECDELIM: starting...\n");
		printf("str = %s\n",str);
		printf("slen = %d\n",slen);
		printf("delim = %s\n",dname);
	}
# endif

	savestr = str;
	savelen = slen;
	*plen = 0;
	/* find correct delimiter in the queue */
	for (d = Delimhead; d != NULL && strcmp(d->delim,dname);d = d->back)
		continue;

	if (d == NULL)
	{
		ov_err(BADDELIM);
	}

	for (k = 0; k < (num & I1MASK); k++)
	{
		if (k)
		{
			start = start - 1 + *plen;
		/* 	savestr = &savestr[start]; */

			for ( i = 0; i < *plen - 1; i++)
			{
				*savestr++;
				savelen--;
			}

		}
		while (savelen > 0)
		{
			map = d->maptr;
			start++;
			*plen = 0;
			str = savestr;
			slen = savelen;
			savestr++;
			savelen--;
			patch = *str++;
			match = TRUE;
	
			while ((map != NULL) && (slen >= 0) && (match))
			{
				switch (map->type)
				{
				    case ONE:
					if (test(map->bits, patch))
					{
						map = map->next;
						patch = *str++;
						slen--;
						(*plen)++;
					}
					else
						match = FALSE;
					break;
	
				    case ZEROMORE:
					while((slen >= 0) && (test(map->bits,patch)))
					{
						patch = *str++;
						slen--;
						(*plen)++;
					}
					map = map->next;
					break;
				}
			}
	
			if ((map == NULL))
			{
				/* pattern was found */
				break;
			}
		}
		if ((slen <= 1) && (map != NULL))
			return(-1);
	}
	return(start);
}
