/*
 *	aegis - project change supervisor
 *	Copyright (C) 1998, 1999, 2002 Peter Miller;
 *	All rights reserved.
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 *
 * MANIFEST: functions to manipulate strings with regular expressions
 */

#include <sys/types.h>
#include <ac/regex.h>

#include <str.h>
#include <stracc.h>


int
str_re_match(string_ty *formal, string_ty *actual,
    void (*error_callback)(const char *))
{
#ifdef HAVE_REGCOMP
    regex_t	    preg;
    int		    err;

    /*
     * compile the regular expression
     */
    err = regcomp(&preg, formal->str_text, REG_EXTENDED | REG_NOSUB);
    if (err != 0)
    {
	bomb:
	if (error_callback)
	{
	    char	    buffer[100];

	    regerror(err, &preg, buffer, sizeof(buffer));
	    error_callback(buffer);
	}
	return -1;
    }

    /*
     * execute the regular expression
     */
    err = regexec(&preg, actual->str_text, (size_t)0, NULL, 0);
    regfree(&preg);
    if (err == REG_NOMATCH)
	return 0;
    if (err != 0)
	goto bomb;
    return 1;
#else
    if (error_callback)
	error_callback("Regular expressions not available.");
    return -1;
#endif
}


string_ty *
str_re_substitute(string_ty *lhs, string_ty *rhs, string_ty *actual,
    void (*error_callback)(const char *), int how_many_times)
{
#ifdef HAVE_REGCOMP
    static stracc_t sa;
    regex_t	    preg;
    int		    err;
    const char	    *ip;
    const char	    *ip_end;
    int		    suppress_on_zero;

    /*
     * compile the regular expression
     */
    err = regcomp(&preg, lhs->str_text, REG_EXTENDED);
    if (err != 0)
    {
	bomb:
	if (error_callback)
	{
	    char	    buffer[100];

	    regerror(err, &preg, buffer, sizeof(buffer));
	    error_callback(buffer);
	}
	return NULL;
    }

    /*
     * execute the regular expression
     */
    if (how_many_times <= 0)
	    how_many_times = actual->str_length + 1;
    ip = actual->str_text;
    ip_end = actual->str_text + actual->str_length;
    stracc_open(&sa);
    suppress_on_zero = 0;
    while (ip < ip_end)
    {
	regmatch_t	match[10];
	char		*cp;

	err =
	    regexec
	    (
		&preg,
		ip,
		SIZEOF(match),
		match,
		(ip == actual->str_text ? 0 : REG_NOTBOL)
	    );
	if (err == REG_NOMATCH)
	    break;
	if (err)
	    goto bomb;

	if (match[0].rm_eo == 0 && suppress_on_zero)
	    goto yuck;

	/*
	 * copy the fixed portion
	 */
	stracc_chars(&sa, ip, match[0].rm_so);

	/*
	 * replace the matched portion with the right hand side
	 */
	for (cp = rhs->str_text; *cp; ++cp)
	{
	    regmatch_t	*rm;

	    switch (*cp)
	    {
	    default:
		stracc_char(&sa, *cp);
		break;

	    case '&':
		rm = &match[0];
		stracc_chars
		(
	    	    &sa,
	    	    ip + rm->rm_so,
	    	    rm->rm_eo - rm->rm_so
		);
		break;

	    case '\\':
		switch (*++cp)
		{
		default:
		    stracc_char(&sa, *cp);
		    break;

		case '0': case '1': case '2': case '3':
		case '4': case '5': case '6': case '7':
		case '8': case '9':
		    rm = &match[*cp - '0'];
		    if (rm->rm_so < 0)
		       	break;
		    stracc_chars(&sa, ip + rm->rm_so, rm->rm_eo - rm->rm_so);
		    break;

		case 0:
		    --cp;
		    break;
		}
		break;
	    }
	}
	suppress_on_zero = 1;

	/*
	 * Move past the matched portion.
	 */
	ip += match[0].rm_eo;

	/*
	 * There is a nasty boundary condition: we need to
	 * move past the infinite loop of a zero-length match.
	 * (This can happen for non-trivial patterns.)
	 */
	if (match[0].rm_so == match[0].rm_eo)
	{
	    yuck:
	    if (ip >= ip_end)
	       	break;
	    stracc_char(&sa, *ip++);
	    suppress_on_zero = 0;
	}

	/*
	 * Limit how many times we go through this loop.
	 */
	if (--how_many_times <= 0)
	    break;
    }

    /*
     * Collect the tail-end of the input.
     */
    stracc_chars(&sa, ip, ip_end - ip);
    regfree(&preg);

    /*
     * Build ther answer.
     */
    return stracc_close(&sa);
#else
    if (error_callback)
	error_callback("Regular expressions not available.");
    return NULL;
#endif
}
