244 lines
4.7 KiB
C
244 lines
4.7 KiB
C
/* regsub.c */
|
|
|
|
/* This file contains the regsub() function, which performs substitutions
|
|
* after a regexp match has been found.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "ctype.h"
|
|
#include "vi.h"
|
|
#include "regexp.h"
|
|
|
|
|
|
/* perform substitutions after a regexp match */
|
|
void regsub(re, src, dst)
|
|
regexp *re; /* the regexp with pointers into matched text */
|
|
REG char *src; /* the replacement string */
|
|
REG char *dst; /* where to put the result of the subst */
|
|
{
|
|
REG char *cpy; /* pointer to start of text to copy */
|
|
REG char *end; /* pointer to end of text to copy */
|
|
REG char c;
|
|
char *start;
|
|
#ifndef CRUNCH
|
|
int mod = 0;/* used to track \U, \L, \u, \l, and \E */
|
|
int len; /* used to calculate length of subst string */
|
|
static char *prev; /* a copy of the text from the previous subst */
|
|
|
|
/* replace \~ (or maybe ~) by previous substitution text */
|
|
|
|
/* step 1: calculate the length of the new substitution text */
|
|
for (len = strlen(src), c = '\0', cpy = src; *cpy; cpy++)
|
|
{
|
|
# ifdef NO_MAGIC
|
|
if (c == '\\' && *cpy == '~')
|
|
# else
|
|
if (c == (*o_magic ? '\0' : '\\') && *cpy == '~')
|
|
# endif
|
|
{
|
|
if (!prev)
|
|
{
|
|
regerror("No prev text to substitute for ~");
|
|
return;
|
|
}
|
|
len += strlen(prev) - 1;
|
|
# ifndef NO_MAGIC
|
|
if (!*o_magic)
|
|
# endif
|
|
len -= 1; /* because we lose the \ too */
|
|
}
|
|
|
|
/* watch backslash quoting */
|
|
if (c != '\\' && *cpy == '\\')
|
|
c = '\\';
|
|
else
|
|
c = '\0';
|
|
}
|
|
|
|
/* allocate memory for the ~ed version of src */
|
|
start = cpy = (char *)malloc((unsigned)(len + 1));
|
|
if (!cpy)
|
|
{
|
|
regerror("Not enough memory for ~ expansion");
|
|
return;
|
|
}
|
|
|
|
/* copy src into start, replacing the ~s by the previous text */
|
|
while (*src)
|
|
{
|
|
# ifndef NO_MAGIC
|
|
if (*o_magic && *src == '~')
|
|
{
|
|
strcpy(cpy, prev);
|
|
cpy += strlen(prev);
|
|
src++;
|
|
}
|
|
else if (!*o_magic && *src == '\\' && *(src + 1) == '~')
|
|
# else /* NO_MAGIC */
|
|
if (*src == '\\' && *(src + 1) == '~')
|
|
# endif /* NO_MAGIC */
|
|
{
|
|
strcpy(cpy, prev);
|
|
cpy += strlen(prev);
|
|
src += 2;
|
|
}
|
|
else
|
|
{
|
|
*cpy++ = *src++;
|
|
}
|
|
}
|
|
*cpy = '\0';
|
|
#ifdef DEBUG
|
|
if ((int)(cpy - start) != len)
|
|
{
|
|
msg("Bug in regsub.c! Predicted length = %d, Actual length = %d", len, (int)(cpy - start));
|
|
}
|
|
#endif
|
|
|
|
/* remember this as the "previous" for next time */
|
|
if (prev)
|
|
free(prev);
|
|
prev = src = start;
|
|
|
|
#endif /* undef CRUNCH */
|
|
|
|
start = src;
|
|
while ((c = *src++) != '\0')
|
|
{
|
|
#ifndef NO_MAGIC
|
|
/* recognize any meta characters */
|
|
if (c == '&' && *o_magic)
|
|
{
|
|
cpy = re->startp[0];
|
|
end = re->endp[0];
|
|
}
|
|
else
|
|
#endif /* not NO_MAGIC */
|
|
if (c == '\\')
|
|
{
|
|
c = *src++;
|
|
switch (c)
|
|
{
|
|
#ifndef NO_MAGIC
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
/* \0 thru \9 mean "copy subexpression" */
|
|
c -= '0';
|
|
cpy = re->startp[c];
|
|
end = re->endp[c];
|
|
break;
|
|
# ifndef CRUNCH
|
|
case 'U':
|
|
case 'u':
|
|
case 'L':
|
|
case 'l':
|
|
/* \U and \L mean "convert to upper/lowercase" */
|
|
mod = c;
|
|
continue;
|
|
|
|
case 'E':
|
|
case 'e':
|
|
/* \E ends the \U or \L */
|
|
mod = 0;
|
|
continue;
|
|
# endif /* not CRUNCH */
|
|
case '&':
|
|
/* "\&" means "original text" */
|
|
if (*o_magic)
|
|
{
|
|
*dst++ = c;
|
|
continue;
|
|
}
|
|
cpy = re->startp[0];
|
|
end = re->endp[0];
|
|
break;
|
|
|
|
#else /* NO_MAGIC */
|
|
case '&':
|
|
/* "\&" means "original text" */
|
|
cpy = re->startp[0];
|
|
end = re->endp[0];
|
|
break;
|
|
#endif /* NO_MAGIC */
|
|
default:
|
|
/* ordinary char preceded by backslash */
|
|
*dst++ = c;
|
|
continue;
|
|
}
|
|
}
|
|
#ifndef CRUNCH
|
|
# if OSK
|
|
else if (c == '\l')
|
|
# else
|
|
else if (c == '\r')
|
|
# endif
|
|
{
|
|
/* transliterate ^M into newline */
|
|
*dst++ = '\n';
|
|
continue;
|
|
}
|
|
#endif /* !CRUNCH */
|
|
else
|
|
{
|
|
/* ordinary character, so just copy it */
|
|
*dst++ = c;
|
|
continue;
|
|
}
|
|
|
|
/* Note: to reach this point in the code, we must have evaded
|
|
* all "continue" statements. To do that, we must have hit
|
|
* a metacharacter that involves copying.
|
|
*/
|
|
|
|
/* if there is nothing to copy, loop */
|
|
if (!cpy)
|
|
continue;
|
|
|
|
/* copy over a portion of the original */
|
|
while (cpy < end)
|
|
{
|
|
#ifndef NO_MAGIC
|
|
# ifndef CRUNCH
|
|
switch (mod)
|
|
{
|
|
case 'U':
|
|
case 'u':
|
|
/* convert to uppercase */
|
|
*dst++ = toupper(*cpy++);
|
|
break;
|
|
|
|
case 'L':
|
|
case 'l':
|
|
/* convert to lowercase */
|
|
*dst++ = tolower(*cpy++);
|
|
break;
|
|
|
|
default:
|
|
/* copy without any conversion */
|
|
*dst++ = *cpy++;
|
|
}
|
|
|
|
/* \u and \l end automatically after the first char */
|
|
if (mod && (mod == 'u' || mod == 'l'))
|
|
{
|
|
mod = 0;
|
|
}
|
|
# else /* CRUNCH */
|
|
*dst++ = *cpy++;
|
|
# endif /* CRUNCH */
|
|
#else /* NO_MAGIC */
|
|
*dst++ = *cpy++;
|
|
#endif /* NO_MAGIC */
|
|
}
|
|
}
|
|
*dst = '\0';
|
|
}
|