/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*          Copyright (c) 1985-2012 AT&T Intellectual Property          *
*          Copyright (c) 2020-2024 Contributors to ksh 93u+m           *
*                      and is licensed under the                       *
*                 Eclipse Public License, Version 2.0                  *
*                                                                      *
*                A copy of the License is available at                 *
*      https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html      *
*         (with md5 checksum 84283fa8859daf213bdda5a9f8d1be1d)         *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                  David Korn <dgk@research.att.com>                   *
*                   Phong Vo <kpv@research.att.com>                    *
*                  Martijn Dekker <martijn@inlv.org>                   *
*            Johnothan King <johnothanking@protonmail.com>             *
*                                                                      *
***********************************************************************/

/*
 * 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, regsub_t* b, const char* ss, regsubop_t* op, size_t nmatch, regmatch_t* match)
{
	char*	s;
	char*	e;
	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)
{
	int		c;
	regsub_t*	b;
	const char*	e;
	int		m;

	if (!p->env->sub || (p->env->flags & REG_NOSUB) || !nmatch)
		return fatal(p->env->disc, REG_BADPAT, NULL);
	b = p->re_sub;
	m = b->re_min;
	b->re_cur = b->re_buf;
	e = (const char*)p->env->end;
	c = 0;
	for (;;)
	{
		if (--m > 0)
			PUTS(p, b, s, match->rm_eo, return fatal(p->env->disc, c, NULL));
		else
		{
			PUTS(p, b, s, match->rm_so, return fatal(p->env->disc, c, NULL));
			if (!c && (c = sub(p, b, s, b->re_ops, nmatch, match)))
				return fatal(p->env->disc, c, NULL);
		}
		s += match->rm_eo;
		if (m <= 0 && !(b->re_flags & REG_SUB_ALL) || !*s)
			break;
		if (c = regnexec(p, s, e - 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, NULL);
			break;
		}
		if (!match->rm_so && !match->rm_eo && *s && m <= 1)
		{
			match->rm_so = match->rm_eo = 1;
			c = 1;
		}
	}
	while (s < e)
	{
		c = *s++;
		PUTC(p, b, c, return fatal(p->env->disc, c, NULL));
	}
	NEED(p, b, 1, return fatal(p->env->disc, c, NULL));
	*b->re_cur = 0;
	b->re_len = b->re_cur - b->re_buf;
	return 0;
}

/*
 * 20120528: regoff_t changed from int to ssize_t
 */

#undef	regsubexec
#define regsubexec	_ast_regsubexec

extern int
regsubexec(const regex_t* p, const char* s, size_t nmatch, oldregmatch_t* oldmatch)
{
	if (oldmatch)
	{
		regmatch_t*	match;
		ssize_t		i;
		int		r;

		if (!(match = oldof(0, regmatch_t, nmatch, 0)))
			return -1;
		for (i = 0; i < nmatch; i++)
		{
			match[i].rm_so = oldmatch[i].rm_so;
			match[i].rm_eo = oldmatch[i].rm_eo;
		}
		if (!(r = regsubexec_20120528(p, s, nmatch, match)))
			for (i = 0; i < nmatch; i++)
			{
				oldmatch[i].rm_so = match[i].rm_so;
				oldmatch[i].rm_eo = match[i].rm_eo;
			}
		free(match);
		return r;
	}
	return regsubexec_20120528(p, s, 0, NULL);
}
