#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "pcre2_internal.h"
#define PTR_STACK_SIZE 20
#define SUBSTITUTE_OPTIONS \
(PCRE2_SUBSTITUTE_EXTENDED|PCRE2_SUBSTITUTE_GLOBAL| \
PCRE2_SUBSTITUTE_LITERAL|PCRE2_SUBSTITUTE_MATCHED| \
PCRE2_SUBSTITUTE_OVERFLOW_LENGTH|PCRE2_SUBSTITUTE_REPLACEMENT_ONLY| \
PCRE2_SUBSTITUTE_UNKNOWN_UNSET|PCRE2_SUBSTITUTE_UNSET_EMPTY)
static int
find_text_end(const pcre2_code *code, PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend,
BOOL last)
{
int rc = 0;
uint32_t nestlevel = 0;
BOOL literal = FALSE;
PCRE2_SPTR ptr = *ptrptr;
for (; ptr < ptrend; ptr++)
{
if (literal)
{
if (ptr[0] == CHAR_BACKSLASH && ptr < ptrend - 1 && ptr[1] == CHAR_E)
{
literal = FALSE;
ptr += 1;
}
}
else if (*ptr == CHAR_RIGHT_CURLY_BRACKET)
{
if (nestlevel == 0) goto EXIT;
nestlevel--;
}
else if (*ptr == CHAR_COLON && !last && nestlevel == 0) goto EXIT;
else if (*ptr == CHAR_DOLLAR_SIGN)
{
if (ptr < ptrend - 1 && ptr[1] == CHAR_LEFT_CURLY_BRACKET)
{
nestlevel++;
ptr += 1;
}
}
else if (*ptr == CHAR_BACKSLASH)
{
int erc;
int errorcode;
uint32_t ch;
if (ptr < ptrend - 1) switch (ptr[1])
{
case CHAR_L:
case CHAR_l:
case CHAR_U:
case CHAR_u:
ptr += 1;
continue;
}
ptr += 1;
erc = PRIV(check_escape)(&ptr, ptrend, &ch, &errorcode,
code->overall_options, code->extra_options, code->top_bracket, FALSE, NULL);
ptr -= 1;
if (errorcode != 0)
{
rc = PCRE2_ERROR_BADREPESCAPE;
goto EXIT;
}
switch(erc)
{
case 0:
case ESC_b:
case ESC_v:
case ESC_E:
break;
case ESC_Q:
literal = TRUE;
break;
case ESC_g:
break;
default:
if (erc < 0)
break;
rc = PCRE2_ERROR_BADREPESCAPE;
goto EXIT;
}
}
}
rc = PCRE2_ERROR_REPMISSINGBRACE;
EXIT:
*ptrptr = ptr;
return rc;
}
static BOOL
read_name_subst(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, BOOL utf,
const uint8_t* ctypes)
{
PCRE2_SPTR ptr = *ptrptr;
PCRE2_SPTR nameptr = ptr;
if (ptr >= ptrend)
goto FAILED;
#ifdef SUPPORT_UNICODE
if (utf)
{
uint32_t c, type;
while (ptr < ptrend)
{
GETCHAR(c, ptr);
type = UCD_CHARTYPE(c);
if (type != ucp_Nd && PRIV(ucp_gentype)[type] != ucp_L &&
c != CHAR_UNDERSCORE) break;
ptr++;
FORWARDCHARTEST(ptr, ptrend);
}
}
else
#else
(void)utf;
#endif
{
while (ptr < ptrend && MAX_255(*ptr) && (ctypes[*ptr] & ctype_word) != 0)
{
ptr++;
}
}
if (ptr - nameptr > MAX_NAME_SIZE)
goto FAILED;
if (ptr == nameptr)
goto FAILED;
*ptrptr = ptr;
return TRUE;
FAILED:
*ptrptr = ptr;
return FALSE;
}
#define PCRE2_SUBSTITUTE_CASE_NONE 0
#define PCRE2_SUBSTITUTE_CASE_REVERSE_TITLE_FIRST 4
typedef struct {
int to_case;
BOOL single_char;
} case_state;
static PCRE2_SIZE
pessimistic_case_inflation(PCRE2_SIZE len)
{
return (len >> 3u) + 10;
}
static PCRE2_SIZE
default_substitute_case_callout(
PCRE2_SPTR input, PCRE2_SIZE input_len,
PCRE2_UCHAR *output, PCRE2_SIZE output_cap,
case_state *state, const pcre2_code *code)
{
PCRE2_SPTR input_end = input + input_len;
#ifdef SUPPORT_UNICODE
BOOL utf;
BOOL ucp;
#endif
PCRE2_UCHAR temp[6];
BOOL next_to_upper;
BOOL rest_to_upper;
BOOL single_char;
BOOL overflow = FALSE;
PCRE2_SIZE written = 0;
PCRE2_ASSERT((char *)(input + input_len) <= (char *)output ||
(char *)(output + output_cap) <= (char *)input);
#ifdef SUPPORT_UNICODE
utf = (code->overall_options & PCRE2_UTF) != 0;
ucp = (code->overall_options & PCRE2_UCP) != 0;
#endif
if (input_len == 0) return 0;
switch (state->to_case)
{
default:
PCRE2_DEBUG_UNREACHABLE();
return 0;
case PCRE2_SUBSTITUTE_CASE_LOWER: case PCRE2_SUBSTITUTE_CASE_UPPER: next_to_upper = rest_to_upper = (state->to_case == PCRE2_SUBSTITUTE_CASE_UPPER);
break;
case PCRE2_SUBSTITUTE_CASE_TITLE_FIRST: next_to_upper = TRUE;
rest_to_upper = FALSE;
state->to_case = PCRE2_SUBSTITUTE_CASE_LOWER;
break;
case PCRE2_SUBSTITUTE_CASE_REVERSE_TITLE_FIRST: next_to_upper = FALSE;
rest_to_upper = TRUE;
state->to_case = PCRE2_SUBSTITUTE_CASE_UPPER;
break;
}
single_char = state->single_char;
if (single_char)
state->to_case = PCRE2_SUBSTITUTE_CASE_NONE;
while (input < input_end)
{
uint32_t ch;
unsigned int chlen;
GETCHARINCTEST(ch, input);
#ifdef SUPPORT_UNICODE
if ((utf || ucp) && ch >= 128)
{
uint32_t type = UCD_CHARTYPE(ch);
if (PRIV(ucp_gentype)[type] == ucp_L &&
type != (next_to_upper? ucp_Lu : ucp_Ll))
ch = UCD_OTHERCASE(ch);
}
else
#endif
if (MAX_255(ch))
{
if (((code->tables + cbits_offset +
(next_to_upper? cbit_upper:cbit_lower)
)[ch/8] & (1u << (ch%8))) == 0)
ch = (code->tables + fcc_offset)[ch];
}
#ifdef SUPPORT_UNICODE
if (utf) chlen = PRIV(ord2utf)(ch, temp); else
#endif
{
temp[0] = ch;
chlen = 1;
}
if (!overflow && chlen <= output_cap)
{
memcpy(output, temp, CU2BYTES(chlen));
output += chlen;
output_cap -= chlen;
}
else
{
overflow = TRUE;
}
if (chlen > ~(PCRE2_SIZE)0 - written)
return ~(PCRE2_SIZE)0;
written += chlen;
next_to_upper = rest_to_upper;
if (single_char)
{
PCRE2_SIZE rest_len = input_end - input;
if (!overflow && rest_len <= output_cap)
memcpy(output, input, CU2BYTES(rest_len));
if (rest_len > ~(PCRE2_SIZE)0 - written)
return ~(PCRE2_SIZE)0;
written += rest_len;
return written;
}
}
return written;
}
static PCRE2_SIZE
do_case_copy(
PCRE2_UCHAR *input_output, PCRE2_SIZE input_len, PCRE2_SIZE output_cap,
case_state *state, BOOL utf,
PCRE2_SIZE (*substitute_case_callout)(PCRE2_SPTR, PCRE2_SIZE, PCRE2_UCHAR *,
PCRE2_SIZE, int, void *),
void *substitute_case_callout_data)
{
PCRE2_SPTR input = input_output;
PCRE2_UCHAR *output = input_output;
PCRE2_SIZE rc;
PCRE2_SIZE rc2;
int ch1_to_case;
int rest_to_case;
PCRE2_UCHAR ch1[6];
PCRE2_SIZE ch1_len;
PCRE2_SPTR rest;
PCRE2_SIZE rest_len;
BOOL ch1_overflow = FALSE;
BOOL rest_overflow = FALSE;
#if PCRE2_CODE_UNIT_WIDTH == 32 || !defined(SUPPORT_UNICODE)
(void)utf;
#endif
PCRE2_ASSERT(input_len != 0);
switch (state->to_case)
{
default:
PCRE2_DEBUG_UNREACHABLE();
return 0;
case PCRE2_SUBSTITUTE_CASE_LOWER: case PCRE2_SUBSTITUTE_CASE_UPPER: case PCRE2_SUBSTITUTE_CASE_TITLE_FIRST:
if (state->single_char == FALSE)
{
rc = substitute_case_callout(input, input_len, output, output_cap,
state->to_case, substitute_case_callout_data);
if (state->to_case == PCRE2_SUBSTITUTE_CASE_TITLE_FIRST)
state->to_case = PCRE2_SUBSTITUTE_CASE_LOWER;
return rc;
}
ch1_to_case = state->to_case;
rest_to_case = PCRE2_SUBSTITUTE_CASE_NONE;
break;
case PCRE2_SUBSTITUTE_CASE_REVERSE_TITLE_FIRST: ch1_to_case = PCRE2_SUBSTITUTE_CASE_LOWER;
rest_to_case = PCRE2_SUBSTITUTE_CASE_UPPER;
break;
}
{
PCRE2_SPTR ch_end = input;
uint32_t ch;
GETCHARINCTEST(ch, ch_end);
(void) ch;
PCRE2_ASSERT(ch_end <= input + input_len && ch_end - input <= 6);
ch1_len = ch_end - input;
memcpy(ch1, input, CU2BYTES(ch1_len));
}
rest = input + ch1_len;
rest_len = input_len - ch1_len;
{
PCRE2_SIZE ch1_cap;
PCRE2_SIZE max_ch1_cap;
ch1_cap = ch1_len;
PCRE2_ASSERT(output_cap >= input_len && input_len >= rest_len);
max_ch1_cap = output_cap - rest_len;
while (TRUE)
{
rc = substitute_case_callout(ch1, ch1_len, output, ch1_cap, ch1_to_case,
substitute_case_callout_data);
if (rc == ~(PCRE2_SIZE)0) return rc;
if (rc <= ch1_cap) break;
if (rc > max_ch1_cap)
{
ch1_overflow = TRUE;
break;
}
memmove(input_output + rc, rest, CU2BYTES(rest_len));
rest = input + rc;
ch1_cap = rc;
}
}
if (rest_to_case == PCRE2_SUBSTITUTE_CASE_NONE)
{
if (!ch1_overflow)
{
PCRE2_ASSERT(rest_len <= output_cap - rc);
memmove(output + rc, rest, CU2BYTES(rest_len));
}
rc2 = rest_len;
state->to_case = PCRE2_SUBSTITUTE_CASE_NONE;
}
else
{
PCRE2_UCHAR dummy[1];
rc2 = substitute_case_callout(rest, rest_len,
ch1_overflow? dummy : output + rc,
ch1_overflow? 0u : output_cap - rc,
rest_to_case, substitute_case_callout_data);
if (rc2 == ~(PCRE2_SIZE)0) return rc2;
if (!ch1_overflow && rc2 > output_cap - rc) rest_overflow = TRUE;
if (ch1_overflow && rc2 < rest_len)
rc2 = rest_len;
state->to_case = PCRE2_SUBSTITUTE_CASE_UPPER;
}
if (rc2 > ~(PCRE2_SIZE)0 - rc)
return ~(PCRE2_SIZE)0;
PCRE2_ASSERT(!(ch1_overflow || rest_overflow) || rc + rc2 > output_cap);
(void)rest_overflow;
return rc + rc2;
}
#define CHECKMEMCPY(from, length_) \
do { \
PCRE2_SIZE chkmc_length = length_; \
if (overflowed) \
{ \
if (chkmc_length > ~(PCRE2_SIZE)0 - extra_needed) \
goto TOOLARGEREPLACE; \
extra_needed += chkmc_length; \
} \
else if (lengthleft < chkmc_length) \
{ \
if ((suboptions & PCRE2_SUBSTITUTE_OVERFLOW_LENGTH) == 0) goto NOROOM; \
overflowed = TRUE; \
extra_needed = chkmc_length - lengthleft; \
} \
else \
{ \
memcpy(buffer + buff_offset, from, CU2BYTES(chkmc_length)); \
buff_offset += chkmc_length; \
lengthleft -= chkmc_length; \
} \
} \
while (0)
#define CHECKCASECPY_BASE(length_, do_call) \
do { \
PCRE2_SIZE chkcc_length = (PCRE2_SIZE)(length_); \
PCRE2_SIZE chkcc_rc; \
do_call \
if (lengthleft < chkcc_rc) \
{ \
if ((suboptions & PCRE2_SUBSTITUTE_OVERFLOW_LENGTH) == 0) goto NOROOM; \
overflowed = TRUE; \
extra_needed = chkcc_rc - lengthleft; \
} \
else \
{ \
buff_offset += chkcc_rc; \
lengthleft -= chkcc_rc; \
} \
} \
while (0)
#define CHECKCASECPY_DEFAULT(from, length_) \
CHECKCASECPY_BASE(length_, { \
chkcc_rc = default_substitute_case_callout(from, chkcc_length, \
buffer + buff_offset, \
overflowed? 0 : lengthleft, \
&forcecase, code); \
if (overflowed) \
{ \
if (chkcc_rc > ~(PCRE2_SIZE)0 - extra_needed) \
goto TOOLARGEREPLACE; \
extra_needed += chkcc_rc; \
break; \
} \
})
#define CHECKCASECPY_CALLOUT(length_) \
CHECKCASECPY_BASE(length_, { \
chkcc_rc = do_case_copy(buffer + buff_offset, chkcc_length, \
lengthleft, &forcecase, utf, \
substitute_case_callout, \
substitute_case_callout_data); \
if (chkcc_rc == ~(PCRE2_SIZE)0) goto CASEERROR; \
})
#define DELAYEDFORCECASE() \
do { \
PCRE2_SIZE chars_outstanding = (buff_offset - casestart_offset) + \
(extra_needed - casestart_extra_needed); \
if (chars_outstanding > 0) \
{ \
if (overflowed) \
{ \
PCRE2_SIZE guess = pessimistic_case_inflation(chars_outstanding); \
if (guess > ~(PCRE2_SIZE)0 - extra_needed) \
goto TOOLARGEREPLACE; \
extra_needed += guess; \
} \
else \
{ \
\
lengthleft += (buff_offset - casestart_offset); \
buff_offset = casestart_offset; \
\
CHECKCASECPY_CALLOUT(chars_outstanding); \
} \
} \
} \
while (0)
PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
pcre2_substitute(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length,
PCRE2_SIZE start_offset, uint32_t options, pcre2_match_data *match_data,
pcre2_match_context *mcontext, PCRE2_SPTR replacement, PCRE2_SIZE rlength,
PCRE2_UCHAR *buffer, PCRE2_SIZE *blength)
{
int rc;
int subs;
uint32_t ovector_count;
uint32_t goptions = 0;
uint32_t suboptions;
pcre2_match_data *internal_match_data = NULL;
BOOL escaped_literal = FALSE;
BOOL overflowed = FALSE;
BOOL use_existing_match;
BOOL replacement_only;
BOOL utf = (code->overall_options & PCRE2_UTF) != 0;
PCRE2_UCHAR temp[6];
PCRE2_SPTR ptr;
PCRE2_SPTR repend = NULL;
PCRE2_SIZE extra_needed = 0;
PCRE2_SIZE buff_offset, buff_length, lengthleft, fraglength;
PCRE2_SIZE *ovector;
PCRE2_SIZE ovecsave[3];
pcre2_substitute_callout_block scb;
PCRE2_SIZE sub_start_extra_needed;
PCRE2_SIZE (*substitute_case_callout)(PCRE2_SPTR, PCRE2_SIZE, PCRE2_UCHAR *,
PCRE2_SIZE, int, void *) = NULL;
void *substitute_case_callout_data = NULL;
buff_offset = 0;
lengthleft = buff_length = *blength;
*blength = PCRE2_UNSET;
ovecsave[0] = ovecsave[1] = ovecsave[2] = PCRE2_UNSET;
if (mcontext != NULL)
{
substitute_case_callout = mcontext->substitute_case_callout;
substitute_case_callout_data = mcontext->substitute_case_callout_data;
}
if ((options & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0)
return PCRE2_ERROR_BADOPTION;
if (replacement == NULL)
{
if (rlength != 0) return PCRE2_ERROR_NULL;
replacement = (PCRE2_SPTR)"";
}
if (rlength == PCRE2_ZERO_TERMINATED) rlength = PRIV(strlen)(replacement);
repend = replacement + rlength;
use_existing_match = ((options & PCRE2_SUBSTITUTE_MATCHED) != 0);
replacement_only = ((options & PCRE2_SUBSTITUTE_REPLACEMENT_ONLY) != 0);
if (match_data == NULL)
{
pcre2_general_context gcontext;
if (use_existing_match) return PCRE2_ERROR_NULL;
gcontext.memctl = (mcontext == NULL)?
((const pcre2_real_code *)code)->memctl :
((pcre2_real_match_context *)mcontext)->memctl;
match_data = internal_match_data =
pcre2_match_data_create_from_pattern(code, &gcontext);
if (internal_match_data == NULL) return PCRE2_ERROR_NOMEMORY;
}
else if (use_existing_match)
{
int pairs;
pcre2_general_context gcontext;
gcontext.memctl = (mcontext == NULL)?
((const pcre2_real_code *)code)->memctl :
((pcre2_real_match_context *)mcontext)->memctl;
pairs = (code->top_bracket + 1 < match_data->oveccount)?
code->top_bracket + 1 : match_data->oveccount;
internal_match_data = pcre2_match_data_create(match_data->oveccount,
&gcontext);
if (internal_match_data == NULL) return PCRE2_ERROR_NOMEMORY;
memcpy(internal_match_data, match_data, offsetof(pcre2_match_data, ovector)
+ 2*pairs*sizeof(PCRE2_SIZE));
internal_match_data->heapframes = NULL;
internal_match_data->heapframes_size = 0;
match_data = internal_match_data;
}
ovector = pcre2_get_ovector_pointer(match_data);
ovector_count = pcre2_get_ovector_count(match_data);
scb.version = 0;
scb.input = subject;
scb.output = (PCRE2_SPTR)buffer;
scb.ovector = ovector;
if (subject == NULL)
{
if (length != 0) return PCRE2_ERROR_NULL;
subject = (PCRE2_SPTR)"";
}
if (length == PCRE2_ZERO_TERMINATED)
length = subject? PRIV(strlen)(subject) : 0;
#ifdef SUPPORT_UNICODE
if (utf && (options & PCRE2_NO_UTF_CHECK) == 0)
{
rc = PRIV(valid_utf)(replacement, rlength, &(match_data->startchar));
if (rc != 0)
{
match_data->leftchar = 0;
goto EXIT;
}
}
#endif
suboptions = options & SUBSTITUTE_OPTIONS;
options &= ~SUBSTITUTE_OPTIONS;
if (start_offset > length)
{
match_data->leftchar = 0;
rc = PCRE2_ERROR_BADOFFSET;
goto EXIT;
}
if (!replacement_only) CHECKMEMCPY(subject, start_offset);
subs = 0;
do
{
PCRE2_SPTR ptrstack[PTR_STACK_SIZE];
uint32_t ptrstackptr = 0;
case_state forcecase = { PCRE2_SUBSTITUTE_CASE_NONE, FALSE };
PCRE2_SIZE casestart_offset = 0;
PCRE2_SIZE casestart_extra_needed = 0;
if (use_existing_match)
{
rc = match_data->rc;
use_existing_match = FALSE;
}
else rc = pcre2_match(code, subject, length, start_offset, options|goptions,
match_data, mcontext);
#ifdef SUPPORT_UNICODE
if (utf) options |= PCRE2_NO_UTF_CHECK;
#endif
if (rc < 0)
{
PCRE2_SIZE save_start;
if (rc != PCRE2_ERROR_NOMATCH) goto EXIT;
if (goptions == 0 || start_offset >= length) break;
save_start = start_offset++;
if (subject[start_offset-1] == CHAR_CR &&
(code->newline_convention == PCRE2_NEWLINE_CRLF ||
code->newline_convention == PCRE2_NEWLINE_ANY ||
code->newline_convention == PCRE2_NEWLINE_ANYCRLF) &&
start_offset < length &&
subject[start_offset] == CHAR_LF)
start_offset++;
else if ((code->overall_options & PCRE2_UTF) != 0)
{
#if PCRE2_CODE_UNIT_WIDTH == 8
while (start_offset < length && (subject[start_offset] & 0xc0) == 0x80)
start_offset++;
#elif PCRE2_CODE_UNIT_WIDTH == 16
while (start_offset < length &&
(subject[start_offset] & 0xfc00) == 0xdc00)
start_offset++;
#endif
}
fraglength = start_offset - save_start;
if (!replacement_only) CHECKMEMCPY(subject + save_start, fraglength);
goptions = 0;
continue;
}
if (ovector[1] < ovector[0] || ovector[0] < start_offset)
{
rc = PCRE2_ERROR_BADSUBSPATTERN;
goto EXIT;
}
if (ovecsave[0] == ovector[0] && ovecsave[1] == ovector[1])
{
if (ovector[0] == ovector[1] && ovecsave[2] != start_offset)
{
goptions = PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED;
ovecsave[2] = start_offset;
continue;
}
rc = PCRE2_ERROR_INTERNAL_DUPMATCH;
goto EXIT;
}
if (subs == INT_MAX)
{
rc = PCRE2_ERROR_TOOMANYREPLACE;
goto EXIT;
}
subs++;
if (rc == 0) rc = ovector_count;
fraglength = ovector[0] - start_offset;
if (!replacement_only) CHECKMEMCPY(subject + start_offset, fraglength);
scb.output_offsets[0] = buff_offset;
scb.oveccount = rc;
sub_start_extra_needed = extra_needed;
ptr = replacement;
if ((suboptions & PCRE2_SUBSTITUTE_LITERAL) != 0)
{
CHECKMEMCPY(ptr, rlength);
}
else for (;;)
{
uint32_t ch;
unsigned int chlen;
int group;
uint32_t special;
PCRE2_SPTR text1_start = NULL;
PCRE2_SPTR text1_end = NULL;
PCRE2_SPTR text2_start = NULL;
PCRE2_SPTR text2_end = NULL;
PCRE2_UCHAR name[MAX_NAME_SIZE + 1];
if (ptr >= repend)
{
if (ptrstackptr == 0) break;
repend = ptrstack[--ptrstackptr];
ptr = ptrstack[--ptrstackptr];
continue;
}
if (escaped_literal)
{
if (ptr[0] == CHAR_BACKSLASH && ptr < repend - 1 && ptr[1] == CHAR_E)
{
escaped_literal = FALSE;
ptr += 2;
continue;
}
goto LOADLITERAL;
}
if (*ptr == CHAR_DOLLAR_SIGN)
{
BOOL inparens;
BOOL inangle;
BOOL star;
PCRE2_SIZE sublength;
PCRE2_UCHAR next;
PCRE2_SPTR subptr, subptrend;
if (++ptr >= repend) goto BAD;
if ((next = *ptr) == CHAR_DOLLAR_SIGN) goto LOADLITERAL;
special = 0;
text1_start = NULL;
text1_end = NULL;
text2_start = NULL;
text2_end = NULL;
group = -1;
inparens = FALSE;
inangle = FALSE;
star = FALSE;
subptr = NULL;
subptrend = NULL;
if (next == CHAR_AMPERSAND)
{
++ptr;
group = 0;
goto GROUP_SUBSTITUTE;
}
if (next == CHAR_GRAVE_ACCENT || next == CHAR_APOSTROPHE)
{
++ptr;
rc = pcre2_substring_length_bynumber(match_data, 0, &sublength);
if (rc < 0) goto PTREXIT;
if (next == CHAR_GRAVE_ACCENT)
{
subptr = subject;
subptrend = subject + ovector[0];
}
else
{
subptr = subject + ovector[1];
subptrend = subject + length;
}
goto SUBPTR_SUBSTITUTE;
}
if (next == CHAR_UNDERSCORE)
{
++ptr;
subptr = subject;
subptrend = subject + length;
goto SUBPTR_SUBSTITUTE;
}
if (next == CHAR_LEFT_CURLY_BRACKET)
{
if (++ptr >= repend) goto BAD;
next = *ptr;
inparens = TRUE;
}
else if (next == CHAR_LESS_THAN_SIGN)
{
if (++ptr >= repend) goto BAD;
next = *ptr;
inangle = TRUE;
}
if (!inangle && next == CHAR_ASTERISK)
{
if (++ptr >= repend) goto BAD;
next = *ptr;
star = TRUE;
}
if (!star && !inangle && next >= CHAR_0 && next <= CHAR_9)
{
group = next - CHAR_0;
while (++ptr < repend)
{
next = *ptr;
if (next < CHAR_0 || next > CHAR_9) break;
group = group * 10 + (next - CHAR_0);
if (group > code->top_bracket)
{
if ((suboptions & PCRE2_SUBSTITUTE_UNKNOWN_UNSET) != 0)
{
while (++ptr < repend && *ptr >= CHAR_0 && *ptr <= CHAR_9);
break;
}
else
{
rc = PCRE2_ERROR_NOSUBSTRING;
goto PTREXIT;
}
}
}
}
else
{
PCRE2_SIZE name_len;
PCRE2_SPTR name_start = ptr;
if (!read_name_subst(&ptr, repend, utf, code->tables + ctypes_offset))
goto BAD;
name_len = ptr - name_start;
memcpy(name, name_start, CU2BYTES(name_len));
name[name_len] = 0;
}
next = 0;
(void)next;
if (inparens)
{
if ((suboptions & PCRE2_SUBSTITUTE_EXTENDED) != 0 &&
!star && ptr < repend - 2 && *ptr == CHAR_COLON)
{
special = *(++ptr);
if (special != CHAR_PLUS && special != CHAR_MINUS)
{
rc = PCRE2_ERROR_BADSUBSTITUTION;
goto PTREXIT;
}
text1_start = ++ptr;
rc = find_text_end(code, &ptr, repend, special == CHAR_MINUS);
if (rc != 0) goto PTREXIT;
text1_end = ptr;
if (special == CHAR_PLUS && *ptr == CHAR_COLON)
{
text2_start = ++ptr;
rc = find_text_end(code, &ptr, repend, TRUE);
if (rc != 0) goto PTREXIT;
text2_end = ptr;
}
}
else
{
if (ptr >= repend || *ptr != CHAR_RIGHT_CURLY_BRACKET)
{
rc = PCRE2_ERROR_REPMISSINGBRACE;
goto PTREXIT;
}
}
ptr++;
}
if (inangle)
{
if (ptr >= repend || *ptr != CHAR_GREATER_THAN_SIGN)
goto BAD;
ptr++;
}
if (star)
{
if (PRIV(strcmp_c8)(name, STRING_MARK) == 0)
{
PCRE2_SPTR mark = pcre2_get_mark(match_data);
if (mark != NULL)
{
fraglength = mark[-1];
if (forcecase.to_case != PCRE2_SUBSTITUTE_CASE_NONE &&
substitute_case_callout == NULL)
CHECKCASECPY_DEFAULT(mark, fraglength);
else
CHECKMEMCPY(mark, fraglength);
}
}
else goto BAD;
}
else
{
GROUP_SUBSTITUTE:
if (group < 0)
{
PCRE2_SPTR first, last, entry;
rc = pcre2_substring_nametable_scan(code, name, &first, &last);
if (rc == PCRE2_ERROR_NOSUBSTRING &&
(suboptions & PCRE2_SUBSTITUTE_UNKNOWN_UNSET) != 0)
{
group = code->top_bracket + 1;
}
else
{
if (rc < 0) goto PTREXIT;
for (entry = first; entry <= last; entry += rc)
{
uint32_t ng = GET2(entry, 0);
if (ng < ovector_count)
{
if (group < 0) group = ng;
if (ovector[ng*2] != PCRE2_UNSET)
{
group = ng;
break;
}
}
}
if (group < 0) group = GET2(first, 0);
}
}
rc = pcre2_substring_length_bynumber(match_data, group, &sublength);
if (rc < 0)
{
if (rc == PCRE2_ERROR_NOSUBSTRING &&
(suboptions & PCRE2_SUBSTITUTE_UNKNOWN_UNSET) != 0)
{
rc = PCRE2_ERROR_UNSET;
}
if (rc != PCRE2_ERROR_UNSET) goto PTREXIT;
if (special == 0)
{
if ((suboptions & PCRE2_SUBSTITUTE_UNSET_EMPTY) != 0) continue;
goto PTREXIT;
}
}
if (special != 0)
{
if (special == CHAR_MINUS)
{
if (rc == 0) goto LITERAL_SUBSTITUTE;
text2_start = text1_start;
text2_end = text1_end;
}
if (ptrstackptr >= PTR_STACK_SIZE) goto BAD;
ptrstack[ptrstackptr++] = ptr;
ptrstack[ptrstackptr++] = repend;
if (rc == 0)
{
ptr = text1_start;
repend = text1_end;
}
else
{
ptr = text2_start;
repend = text2_end;
}
continue;
}
LITERAL_SUBSTITUTE:
subptr = subject + ovector[group*2];
subptrend = subject + ovector[group*2 + 1];
SUBPTR_SUBSTITUTE:
if (forcecase.to_case != PCRE2_SUBSTITUTE_CASE_NONE &&
substitute_case_callout == NULL)
CHECKCASECPY_DEFAULT(subptr, subptrend - subptr);
else
CHECKMEMCPY(subptr, subptrend - subptr);
}
}
else if ((suboptions & PCRE2_SUBSTITUTE_EXTENDED) != 0 &&
*ptr == CHAR_BACKSLASH)
{
int errorcode;
case_state new_forcecase = { PCRE2_SUBSTITUTE_CASE_NONE, FALSE };
if (ptr < repend - 1) switch (ptr[1])
{
case CHAR_L:
new_forcecase.to_case = PCRE2_SUBSTITUTE_CASE_LOWER;
new_forcecase.single_char = FALSE;
ptr += 2;
break;
case CHAR_l:
new_forcecase.to_case = PCRE2_SUBSTITUTE_CASE_LOWER;
new_forcecase.single_char = TRUE;
ptr += 2;
if (ptr + 2 < repend && ptr[0] == CHAR_BACKSLASH && ptr[1] == CHAR_U)
{
new_forcecase.to_case = PCRE2_SUBSTITUTE_CASE_REVERSE_TITLE_FIRST;
new_forcecase.single_char = FALSE;
ptr += 2;
}
break;
case CHAR_U:
new_forcecase.to_case = PCRE2_SUBSTITUTE_CASE_UPPER;
new_forcecase.single_char = FALSE;
ptr += 2;
break;
case CHAR_u:
new_forcecase.to_case = PCRE2_SUBSTITUTE_CASE_TITLE_FIRST;
new_forcecase.single_char = TRUE;
ptr += 2;
if (ptr + 2 < repend && ptr[0] == CHAR_BACKSLASH && ptr[1] == CHAR_L)
{
new_forcecase.to_case = PCRE2_SUBSTITUTE_CASE_TITLE_FIRST;
new_forcecase.single_char = FALSE;
ptr += 2;
}
break;
default:
break;
}
if (new_forcecase.to_case != PCRE2_SUBSTITUTE_CASE_NONE)
{
SETFORCECASE:
if (substitute_case_callout != NULL &&
forcecase.to_case != PCRE2_SUBSTITUTE_CASE_NONE)
DELAYEDFORCECASE();
forcecase = new_forcecase;
casestart_offset = buff_offset;
casestart_extra_needed = extra_needed;
continue;
}
ptr++;
rc = PRIV(check_escape)(&ptr, repend, &ch, &errorcode,
code->overall_options, code->extra_options, code->top_bracket, FALSE, NULL);
if (errorcode != 0) goto BADESCAPE;
switch(rc)
{
case ESC_E:
goto SETFORCECASE;
case ESC_Q:
escaped_literal = TRUE;
continue;
case 0:
case ESC_b:
case ESC_v:
if (rc == ESC_b) ch = CHAR_BS;
if (rc == ESC_v) ch = CHAR_VT;
#ifdef SUPPORT_UNICODE
if (utf) chlen = PRIV(ord2utf)(ch, temp); else
#endif
{
temp[0] = ch;
chlen = 1;
}
if (forcecase.to_case != PCRE2_SUBSTITUTE_CASE_NONE &&
substitute_case_callout == NULL)
CHECKCASECPY_DEFAULT(temp, chlen);
else
CHECKMEMCPY(temp, chlen);
continue;
case ESC_g:
{
PCRE2_SIZE name_len;
PCRE2_SPTR name_start;
if (ptr >= repend || *ptr != CHAR_LESS_THAN_SIGN)
goto BADESCAPE;
++ptr;
name_start = ptr;
if (!read_name_subst(&ptr, repend, utf, code->tables + ctypes_offset))
goto BADESCAPE;
name_len = ptr - name_start;
if (ptr >= repend || *ptr != CHAR_GREATER_THAN_SIGN)
goto BADESCAPE;
++ptr;
special = 0;
group = -1;
memcpy(name, name_start, CU2BYTES(name_len));
name[name_len] = 0;
goto GROUP_SUBSTITUTE;
}
default:
if (rc < 0)
{
special = 0;
group = -rc - 1;
goto GROUP_SUBSTITUTE;
}
goto BADESCAPE;
}
}
else
{
PCRE2_SPTR ch_start;
LOADLITERAL:
ch_start = ptr;
GETCHARINCTEST(ch, ptr);
(void) ch;
if (forcecase.to_case != PCRE2_SUBSTITUTE_CASE_NONE &&
substitute_case_callout == NULL)
CHECKCASECPY_DEFAULT(ch_start, ptr - ch_start);
else
CHECKMEMCPY(ch_start, ptr - ch_start);
}
}
if (substitute_case_callout != NULL &&
forcecase.to_case != PCRE2_SUBSTITUTE_CASE_NONE)
DELAYEDFORCECASE();
if (mcontext != NULL && mcontext->substitute_callout != NULL)
{
if (!overflowed)
{
scb.subscount = subs;
scb.output_offsets[1] = buff_offset;
rc = mcontext->substitute_callout(&scb,
mcontext->substitute_callout_data);
if (rc != 0)
{
PCRE2_SIZE newlength = scb.output_offsets[1] - scb.output_offsets[0];
PCRE2_SIZE oldlength = ovector[1] - ovector[0];
buff_offset -= newlength;
lengthleft += newlength;
if (!replacement_only) CHECKMEMCPY(subject + ovector[0], oldlength);
if (rc < 0) suboptions &= (~PCRE2_SUBSTITUTE_GLOBAL);
}
}
else
{
PCRE2_SIZE newlength_buf = buff_offset - scb.output_offsets[0];
PCRE2_SIZE newlength_extra = extra_needed - sub_start_extra_needed;
PCRE2_SIZE newlength =
(newlength_extra > ~(PCRE2_SIZE)0 - newlength_buf)?
~(PCRE2_SIZE)0 : newlength_buf + newlength_extra;
PCRE2_SIZE oldlength = ovector[1] - ovector[0];
if (oldlength > newlength)
{
PCRE2_SIZE additional = oldlength - newlength;
if (additional > ~(PCRE2_SIZE)0 - extra_needed)
goto TOOLARGEREPLACE;
extra_needed += additional;
}
}
}
ovecsave[0] = ovector[0];
ovecsave[1] = ovector[1];
ovecsave[2] = start_offset;
goptions = (ovector[0] != ovector[1] || ovector[0] > start_offset)? 0 :
PCRE2_ANCHORED|PCRE2_NOTEMPTY_ATSTART;
start_offset = ovector[1];
} while ((suboptions & PCRE2_SUBSTITUTE_GLOBAL) != 0);
if (!replacement_only)
{
fraglength = length - start_offset;
CHECKMEMCPY(subject + start_offset, fraglength);
}
temp[0] = 0;
CHECKMEMCPY(temp, 1);
if (overflowed)
{
rc = PCRE2_ERROR_NOMEMORY;
if (extra_needed > ~(PCRE2_SIZE)0 - buff_length)
goto TOOLARGEREPLACE;
*blength = buff_length + extra_needed;
}
else
{
rc = subs;
*blength = buff_offset - 1;
}
EXIT:
if (internal_match_data != NULL) pcre2_match_data_free(internal_match_data);
else match_data->rc = rc;
return rc;
NOROOM:
rc = PCRE2_ERROR_NOMEMORY;
goto EXIT;
CASEERROR:
rc = PCRE2_ERROR_REPLACECASE;
goto EXIT;
TOOLARGEREPLACE:
rc = PCRE2_ERROR_TOOLARGEREPLACE;
goto EXIT;
BAD:
rc = PCRE2_ERROR_BADREPLACEMENT;
goto PTREXIT;
BADESCAPE:
rc = PCRE2_ERROR_BADREPESCAPE;
PTREXIT:
*blength = (PCRE2_SIZE)(ptr - replacement);
goto EXIT;
}