/*
   gsub - global regular expression search and substitute

   Act like AWK function of same name.

   $Id: gsub.c,v 1.2 1996/07/06 15:01:05 jrv Exp jrv $

   $Log$
   Revision 1.2  1996/07/06 15:01:05  jrv
   Returns nonzero result on error, instead of printing message and
   exiting.  Last parameter is pointer to string pointer, which is
   updated if the string was enlarged.
   
   Revision 1.1  1996/07/06 14:20:22  jrv
   Initial revision

*/

#include <config.h>
#include <regexp.h>
#include <stdlib.h>

/* The array t is currently n bytes long, and holds a string of length
   m.  If we can't append k more bytes to the string without
   overrunning the array, then enlarge the array.
*/
#define ENLARGE_STRING				\
      if (m + k + 1 > n)			\
	{					\
	  u = realloc(t, n *= 2);		\
	  if (u == NULL)			\
	    {					\
	      *sp = t;				\
	      return 1;				\
	    }					\
	  else					\
	    t = u;				\
	}
      
/* searches for the regular expression in the string pointed to by
   *sp, and replaces each instance using the replacement, which may
   include items like \2, which would be replaced by the 2nd
   subexpression in the regexp - see regsub(3).  The string must have
   been allocated from the heap, and the string pointer may be
   modified if the replacement makes the string grow.  Returns nonzero
   on failure.  */
int
gsub(char *exp, char *replacement, char **sp)
{
  char *s = *sp, *t, *u, work[100];
  int n, m, k;
  regexp *re;
  n = strlen(s)+100;
  t = malloc(n);
  if (t == NULL) return 1;
  m = 0;
  re = regcomp(exp);
  while(*s && regexec(re, s))
    {				/* found a match for the regular
                                   expression.  copy the part before
                                   the match */
      k = re->startp[0]-s;
      ENLARGE_STRING
      memcpy(t + m, s, k);
      s += k;
      m += k;
				/* make the substitution using work
                                   area, then copy it to the result */
      regsub(re, replacement, work);
      k = strlen(work);
      ENLARGE_STRING
      memcpy(t + m, work, k);
      s = re->endp[0];
      m += k;
    }
  if (*s)
    {
				/* copy the part after the last match */
      k = strlen(s);
      ENLARGE_STRING
      memcpy(t + m, s, k);
      m += k;
    }
  t[m] = 0;
  *sp = t;
  return 0;
}
      
      
#ifdef TESTING

#include <stdio.h>

/* Read from four successive lines of the input file: a string, a
   regular expression, a replacement string, and the result of
   performing the replacement.  The printout should match the input.
   Any exception is flagged by a final '*'.
*/
int main(argc, argv)
{
  char *string, pattern[64], repl[64], answer[256], *result;
  int flag;

  while((string=malloc(128)) 
	&& gets(string) 
	&& gets(pattern) 
	&& gets(repl) 
	&& gets(answer))
    {
      printf("%s\n%s\n%s\n", string, pattern, repl);
      (void)gsub(pattern, repl, &string);
      flag = strcmp(answer, string);
      printf("%s%s\n", string, flag?" *":"");
      free(string);
    }
  return 0;
}

#endif /* TESTING */
