/*******************************************************************
*                                                                  *
*             This software is part of the ast package             *
*                Copyright (c) 1985-2003 AT&T Corp.                *
*        and it may only be used by you under license from         *
*                       AT&T Corp. ("AT&T")                        *
*         A copy of the Source Code Agreement is available         *
*                at the AT&T Internet web site URL                 *
*                                                                  *
*       http://www.research.att.com/sw/license/ast-open.html       *
*                                                                  *
*    If you have copied or used this software without agreeing     *
*        to the terms of the license you are infringing on         *
*           the license and copyright and are violating            *
*               AT&T's intellectual property rights.               *
*                                                                  *
*            Information and Software Systems Research             *
*                        AT&T Labs Research                        *
*                         Florham Park NJ                          *
*                                                                  *
*               Glenn Fowler <gsf@research.att.com>                *
*                David Korn <dgk@research.att.com>                 *
*                 Phong Vo <kpv@research.att.com>                  *
*                                                                  *
*******************************************************************/
#pragma prototyped

/*
 * posix regex ed(1) style substitute execute
 */

#include "reglib.h"

#define NEED(p,b,n,r)	\
	do \
	{ \
		if (((b)->re_end - (b)->re_cur) < (n)) \
		{ \
			size_t	o = (b)->re_cur - (b)->re_buf; \
			size_t	a = ((b)->re_end - (b)->re_buf); \
			if (a < n) \
				a = roundof(n, 128); \
			a *= 2; \
			if (!((b)->re_buf = alloc(p->env->disc, (b)->re_buf, a))) \
			{ \
				(b)->re_buf = (b)->re_cur = (b)->re_end = 0; \
				c = REG_ESPACE; \
				r; \
			} \
			(b)->re_cur = (b)->re_buf + o; \
			(b)->re_end = (b)->re_buf + a; \
		} \
	} while (0)

#define PUTC(p,b,x,r)	\
	do \
	{ \
		NEED(p, b, 1, r); \
		*(b)->re_cur++ = (x); \
	} while (0)

#define PUTS(p,b,x,z,r)	\
	do if (z) \
	{ \
		NEED(p, b, z, r); \
		memcpy((b)->re_cur, x, z); \
		(b)->re_cur += (z); \
	} while (0)

/*
 * do a single substitution
 */

static int
sub(const regex_t* p, register regsub_t* b, const char* ss, register regsubop_t* op, size_t nmatch, register regmatch_t* match)
{
	register char*	s;
	register char*	e;
	register int	c;

	for (;; op++)
	{
		switch (op->len)
		{
		case -1:
			break;
		case 0:
			if (op->off >= nmatch)
				return REG_ESUBREG;
			if ((c = match[op->off].rm_so) < 0)
				continue;
			s = (char*)ss + c;
			if ((c = match[op->off].rm_eo) < 0)
				continue;
			e = (char*)ss + c;
			NEED(p, b, e - s, return c);
			switch (op->op)
			{
			case REG_SUB_UPPER:
				while (s < e)
				{
					c = *s++;
					if (islower(c))
						c = toupper(c);
					*b->re_cur++ = c;
				}
				break;
			case REG_SUB_LOWER:
				while (s < e)
				{
					c = *s++;
					if (isupper(c))
						c = tolower(c);
					*b->re_cur++ = c;
				}
				break;
			case REG_SUB_UPPER|REG_SUB_LOWER:
				while (s < e)
				{
					c = *s++;
					if (isupper(c))
						c = tolower(c);
					else if (islower(c))
						c = toupper(c);
					*b->re_cur++ = c;
				}
				break;
			default:
				while (s < e)
					*b->re_cur++ = *s++;
				break;
			}
			continue;
		default:
			NEED(p, b, op->len, return c);
			s = b->re_rhs + op->off;
			e = s + op->len;
			while (s < e)
				*b->re_cur++ = *s++;
			continue;
		}
		break;
	}
	return 0;
}

/*
 * ed(1) style substitute using matches from last regexec()
 */

int
regsubexec(const regex_t* p, const char* s, size_t nmatch, regmatch_t* match)
{
	register int		c;
	register regsub_t*	b;
	int			m;

	if (!p->env->sub || (p->env->flags & REG_NOSUB) || !nmatch)
		return fatal(p->env->disc, REG_BADPAT, NiL);
	b = p->re_sub;
	m = b->re_min;
	b->re_cur = b->re_buf;
	for (;;)
	{
		if (--m > 0)
			PUTS(p, b, s, match->rm_eo, return fatal(p->env->disc, c, NiL));
		else
		{
			PUTS(p, b, s, match->rm_so, return fatal(p->env->disc, c, NiL));
			if (c = sub(p, b, s, b->re_ops, nmatch, match))
				return fatal(p->env->disc, c, NiL);
		}
		s += match->rm_eo;
		if (m <= 0 && !(b->re_flags & REG_SUB_ALL))
			break;
		if (c = regexec(p, s, nmatch, match, p->env->flags|(match->rm_so == match->rm_eo ? REG_ADVANCE : 0)))
		{
			if (c != REG_NOMATCH)
				return fatal(p->env->disc, c, NiL);
			break;
		}
	}
	while (c = *s++)
		PUTC(p, b, c, return fatal(p->env->disc, c, NiL));
	NEED(p, b, 1, return fatal(p->env->disc, c, NiL));
	*b->re_cur = 0;
	b->re_len = b->re_cur - b->re_buf;
	return 0;
}
