#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "pcre2_internal.h"
#define MAX_CACHE_BACKREF 128
#define SET_BIT(c) re->start_bitmap[(c)/8] |= (1u << ((c)&7))
enum { SSB_FAIL, SSB_DONE, SSB_CONTINUE, SSB_UNKNOWN, SSB_TOODEEP };
static int
find_minlength(const pcre2_real_code *re, PCRE2_SPTR code,
PCRE2_SPTR startcode, BOOL utf, recurse_check *recurses, int *countptr,
int *backref_cache)
{
int length = -1;
int branchlength = 0;
int prev_cap_recno = -1;
int prev_cap_d = 0;
int prev_recurse_recno = -1;
int prev_recurse_d = 0;
uint32_t once_fudge = 0;
BOOL had_recurse = FALSE;
BOOL dupcapused = (re->flags & PCRE2_DUPCAPUSED) != 0;
PCRE2_SPTR nextbranch = code + GET(code, 1);
PCRE2_SPTR cc = code + 1 + LINK_SIZE;
recurse_check this_recurse;
if (*code >= OP_SBRA && *code <= OP_SCOND) return 0;
if (*code == OP_CBRA || *code == OP_CBRAPOS) cc += IMM2_SIZE;
if ((*countptr)++ > 1000) return -1;
for (;;)
{
int d, min, recno;
PCRE2_UCHAR op;
PCRE2_SPTR cs, ce;
if (branchlength >= UINT16_MAX)
{
branchlength = UINT16_MAX;
cc = nextbranch;
}
op = *cc;
switch (op)
{
case OP_COND:
case OP_SCOND:
cs = cc + GET(cc, 1);
if (*cs != OP_ALT)
{
cc = cs + 1 + LINK_SIZE;
break;
}
goto PROCESS_NON_CAPTURE;
case OP_BRA:
if (cc[1+LINK_SIZE] == OP_RECURSE && cc[2*(1+LINK_SIZE)] == OP_KET)
{
once_fudge = 1 + LINK_SIZE;
cc += 1 + LINK_SIZE;
break;
}
case OP_ONCE:
case OP_SCRIPT_RUN:
case OP_SBRA:
case OP_BRAPOS:
case OP_SBRAPOS:
PROCESS_NON_CAPTURE:
d = find_minlength(re, cc, startcode, utf, recurses, countptr,
backref_cache);
if (d < 0) return d;
branchlength += d;
do cc += GET(cc, 1); while (*cc == OP_ALT);
cc += 1 + LINK_SIZE;
break;
case OP_CBRA:
case OP_SCBRA:
case OP_CBRAPOS:
case OP_SCBRAPOS:
recno = (int)GET2(cc, 1+LINK_SIZE);
if (dupcapused || recno != prev_cap_recno)
{
prev_cap_recno = recno;
prev_cap_d = find_minlength(re, cc, startcode, utf, recurses, countptr,
backref_cache);
if (prev_cap_d < 0) return prev_cap_d;
}
branchlength += prev_cap_d;
do cc += GET(cc, 1); while (*cc == OP_ALT);
cc += 1 + LINK_SIZE;
break;
case OP_ACCEPT:
case OP_ASSERT_ACCEPT:
return -1;
case OP_ALT:
case OP_KET:
case OP_KETRMAX:
case OP_KETRMIN:
case OP_KETRPOS:
case OP_END:
if (length < 0 || (!had_recurse && branchlength < length))
length = branchlength;
if (op != OP_ALT || length == 0) return length;
nextbranch = cc + GET(cc, 1);
cc += 1 + LINK_SIZE;
branchlength = 0;
had_recurse = FALSE;
break;
case OP_ASSERT:
case OP_ASSERT_NOT:
case OP_ASSERTBACK:
case OP_ASSERTBACK_NOT:
case OP_ASSERT_NA:
case OP_ASSERT_SCS:
case OP_ASSERTBACK_NA:
do cc += GET(cc, 1); while (*cc == OP_ALT);
case OP_REVERSE:
case OP_VREVERSE:
case OP_CREF:
case OP_DNCREF:
case OP_RREF:
case OP_DNRREF:
case OP_FALSE:
case OP_TRUE:
case OP_CALLOUT:
case OP_SOD:
case OP_SOM:
case OP_EOD:
case OP_EODN:
case OP_CIRC:
case OP_CIRCM:
case OP_DOLL:
case OP_DOLLM:
case OP_NOT_WORD_BOUNDARY:
case OP_WORD_BOUNDARY:
case OP_NOT_UCP_WORD_BOUNDARY:
case OP_UCP_WORD_BOUNDARY:
cc += PRIV(OP_lengths)[*cc];
break;
case OP_CALLOUT_STR:
cc += GET(cc, 1 + 2*LINK_SIZE);
break;
case OP_BRAZERO:
case OP_BRAMINZERO:
case OP_BRAPOSZERO:
case OP_SKIPZERO:
cc += PRIV(OP_lengths)[*cc];
do cc += GET(cc, 1); while (*cc == OP_ALT);
cc += 1 + LINK_SIZE;
break;
case OP_CHAR:
case OP_CHARI:
case OP_NOT:
case OP_NOTI:
case OP_PLUS:
case OP_PLUSI:
case OP_MINPLUS:
case OP_MINPLUSI:
case OP_POSPLUS:
case OP_POSPLUSI:
case OP_NOTPLUS:
case OP_NOTPLUSI:
case OP_NOTMINPLUS:
case OP_NOTMINPLUSI:
case OP_NOTPOSPLUS:
case OP_NOTPOSPLUSI:
branchlength++;
cc += 2;
#ifdef SUPPORT_UNICODE
if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
#endif
break;
case OP_TYPEPLUS:
case OP_TYPEMINPLUS:
case OP_TYPEPOSPLUS:
branchlength++;
cc += (cc[1] == OP_PROP || cc[1] == OP_NOTPROP)? 4 : 2;
break;
case OP_EXACT:
case OP_EXACTI:
case OP_NOTEXACT:
case OP_NOTEXACTI:
branchlength += GET2(cc,1);
cc += 2 + IMM2_SIZE;
#ifdef SUPPORT_UNICODE
if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
#endif
break;
case OP_TYPEEXACT:
branchlength += GET2(cc,1);
cc += 2 + IMM2_SIZE + ((cc[1 + IMM2_SIZE] == OP_PROP
|| cc[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0);
break;
case OP_PROP:
case OP_NOTPROP:
cc += 2;
case OP_NOT_DIGIT:
case OP_DIGIT:
case OP_NOT_WHITESPACE:
case OP_WHITESPACE:
case OP_NOT_WORDCHAR:
case OP_WORDCHAR:
case OP_ANY:
case OP_ALLANY:
case OP_EXTUNI:
case OP_HSPACE:
case OP_NOT_HSPACE:
case OP_VSPACE:
case OP_NOT_VSPACE:
branchlength++;
cc++;
break;
case OP_ANYNL:
branchlength += 1;
cc++;
break;
case OP_ANYBYTE:
#ifdef SUPPORT_UNICODE
if (utf) return -1;
#endif
branchlength++;
cc++;
break;
case OP_TYPESTAR:
case OP_TYPEMINSTAR:
case OP_TYPEQUERY:
case OP_TYPEMINQUERY:
case OP_TYPEPOSSTAR:
case OP_TYPEPOSQUERY:
if (cc[1] == OP_PROP || cc[1] == OP_NOTPROP) cc += 2;
cc += PRIV(OP_lengths)[op];
break;
case OP_TYPEUPTO:
case OP_TYPEMINUPTO:
case OP_TYPEPOSUPTO:
if (cc[1 + IMM2_SIZE] == OP_PROP
|| cc[1 + IMM2_SIZE] == OP_NOTPROP) cc += 2;
cc += PRIV(OP_lengths)[op];
break;
case OP_CLASS:
case OP_NCLASS:
#ifdef SUPPORT_WIDE_CHARS
case OP_XCLASS:
case OP_ECLASS:
if (op == OP_XCLASS || op == OP_ECLASS)
cc += GET(cc, 1);
else
#endif
cc += PRIV(OP_lengths)[OP_CLASS];
switch (*cc)
{
case OP_CRPLUS:
case OP_CRMINPLUS:
case OP_CRPOSPLUS:
branchlength++;
case OP_CRSTAR:
case OP_CRMINSTAR:
case OP_CRQUERY:
case OP_CRMINQUERY:
case OP_CRPOSSTAR:
case OP_CRPOSQUERY:
cc++;
break;
case OP_CRRANGE:
case OP_CRMINRANGE:
case OP_CRPOSRANGE:
branchlength += GET2(cc,1);
cc += 1 + 2 * IMM2_SIZE;
break;
default:
branchlength++;
break;
}
break;
case OP_DNREF:
case OP_DNREFI:
if (!dupcapused && (re->overall_options & PCRE2_MATCH_UNSET_BACKREF) == 0)
{
int count = GET2(cc, 1+IMM2_SIZE);
PCRE2_SPTR slot =
(PCRE2_SPTR)((const uint8_t *)re + sizeof(pcre2_real_code)) +
GET2(cc, 1) * re->name_entry_size;
d = INT_MAX;
while (count-- > 0)
{
int dd, i;
recno = GET2(slot, 0);
if (recno <= backref_cache[0] && backref_cache[recno] >= 0)
dd = backref_cache[recno];
else
{
ce = cs = PRIV(find_bracket)(startcode, utf, recno);
if (cs == NULL) return -2;
do ce += GET(ce, 1); while (*ce == OP_ALT);
dd = 0;
if (!dupcapused || PRIV(find_bracket)(ce, utf, recno) == NULL)
{
if (cc > cs && cc < ce)
{
had_recurse = TRUE;
}
else
{
recurse_check *r = recurses;
for (r = recurses; r != NULL; r = r->prev)
if (r->group == cs) break;
if (r != NULL)
{
had_recurse = TRUE;
}
else
{
this_recurse.prev = recurses;
this_recurse.group = cs;
dd = find_minlength(re, cs, startcode, utf, &this_recurse,
countptr, backref_cache);
if (dd < 0) return dd;
}
}
}
backref_cache[recno] = dd;
for (i = backref_cache[0] + 1; i < recno; i++) backref_cache[i] = -1;
backref_cache[0] = recno;
}
if (dd < d) d = dd;
if (d <= 0) break;
slot += re->name_entry_size;
}
}
else d = 0;
cc += PRIV(OP_lengths)[*cc];
goto REPEAT_BACK_REFERENCE;
case OP_REF:
case OP_REFI:
recno = GET2(cc, 1);
if (recno <= backref_cache[0] && backref_cache[recno] >= 0)
d = backref_cache[recno];
else
{
int i;
d = 0;
if ((re->overall_options & PCRE2_MATCH_UNSET_BACKREF) == 0)
{
ce = cs = PRIV(find_bracket)(startcode, utf, recno);
if (cs == NULL) return -2;
do ce += GET(ce, 1); while (*ce == OP_ALT);
if (!dupcapused || PRIV(find_bracket)(ce, utf, recno) == NULL)
{
if (cc > cs && cc < ce)
{
had_recurse = TRUE;
}
else
{
recurse_check *r = recurses;
for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break;
if (r != NULL)
{
had_recurse = TRUE;
}
else
{
this_recurse.prev = recurses;
this_recurse.group = cs;
d = find_minlength(re, cs, startcode, utf, &this_recurse, countptr,
backref_cache);
if (d < 0) return d;
}
}
}
}
backref_cache[recno] = d;
for (i = backref_cache[0] + 1; i < recno; i++) backref_cache[i] = -1;
backref_cache[0] = recno;
}
cc += PRIV(OP_lengths)[*cc];
REPEAT_BACK_REFERENCE:
switch (*cc)
{
case OP_CRSTAR:
case OP_CRMINSTAR:
case OP_CRQUERY:
case OP_CRMINQUERY:
case OP_CRPOSSTAR:
case OP_CRPOSQUERY:
min = 0;
cc++;
break;
case OP_CRPLUS:
case OP_CRMINPLUS:
case OP_CRPOSPLUS:
min = 1;
cc++;
break;
case OP_CRRANGE:
case OP_CRMINRANGE:
case OP_CRPOSRANGE:
min = GET2(cc, 1);
cc += 1 + 2 * IMM2_SIZE;
break;
default:
min = 1;
break;
}
if ((d > 0 && (INT_MAX/d) < min) || UINT16_MAX - branchlength < min*d)
branchlength = UINT16_MAX;
else branchlength += min * d;
break;
case OP_RECURSE:
cs = ce = startcode + GET(cc, 1);
recno = GET2(cs, 1+LINK_SIZE);
if (recno == prev_recurse_recno)
{
branchlength += prev_recurse_d;
}
else
{
do ce += GET(ce, 1); while (*ce == OP_ALT);
if (cc > cs && cc < ce)
had_recurse = TRUE;
else
{
recurse_check *r = recurses;
for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break;
if (r != NULL)
had_recurse = TRUE;
else
{
this_recurse.prev = recurses;
this_recurse.group = cs;
prev_recurse_d = find_minlength(re, cs, startcode, utf, &this_recurse,
countptr, backref_cache);
if (prev_recurse_d < 0) return prev_recurse_d;
prev_recurse_recno = recno;
branchlength += prev_recurse_d;
}
}
}
cc += 1 + LINK_SIZE + once_fudge;
once_fudge = 0;
break;
case OP_UPTO:
case OP_UPTOI:
case OP_NOTUPTO:
case OP_NOTUPTOI:
case OP_MINUPTO:
case OP_MINUPTOI:
case OP_NOTMINUPTO:
case OP_NOTMINUPTOI:
case OP_POSUPTO:
case OP_POSUPTOI:
case OP_NOTPOSUPTO:
case OP_NOTPOSUPTOI:
case OP_STAR:
case OP_STARI:
case OP_NOTSTAR:
case OP_NOTSTARI:
case OP_MINSTAR:
case OP_MINSTARI:
case OP_NOTMINSTAR:
case OP_NOTMINSTARI:
case OP_POSSTAR:
case OP_POSSTARI:
case OP_NOTPOSSTAR:
case OP_NOTPOSSTARI:
case OP_QUERY:
case OP_QUERYI:
case OP_NOTQUERY:
case OP_NOTQUERYI:
case OP_MINQUERY:
case OP_MINQUERYI:
case OP_NOTMINQUERY:
case OP_NOTMINQUERYI:
case OP_POSQUERY:
case OP_POSQUERYI:
case OP_NOTPOSQUERY:
case OP_NOTPOSQUERYI:
cc += PRIV(OP_lengths)[op];
#ifdef SUPPORT_UNICODE
if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
#endif
break;
case OP_MARK:
case OP_COMMIT_ARG:
case OP_PRUNE_ARG:
case OP_SKIP_ARG:
case OP_THEN_ARG:
cc += PRIV(OP_lengths)[op] + cc[1];
break;
case OP_CLOSE:
case OP_COMMIT:
case OP_FAIL:
case OP_PRUNE:
case OP_SET_SOM:
case OP_SKIP:
case OP_THEN:
cc += PRIV(OP_lengths)[op];
break;
default:
PCRE2_DEBUG_UNREACHABLE();
return -3;
}
}
PCRE2_DEBUG_UNREACHABLE();
return -3;
}
static PCRE2_SPTR
set_table_bit(pcre2_real_code *re, PCRE2_SPTR p, BOOL caseless, BOOL utf,
BOOL ucp)
{
uint32_t c = *p++;
(void)utf;
(void)ucp;
#if PCRE2_CODE_UNIT_WIDTH != 8
if (c > 0xff) SET_BIT(0xff); else
#endif
SET_BIT(c);
#ifdef SUPPORT_UNICODE
if (utf)
{
#if PCRE2_CODE_UNIT_WIDTH == 8
if (c >= 0xc0) GETUTF8INC(c, p);
#elif PCRE2_CODE_UNIT_WIDTH == 16
if ((c & 0xfc00) == 0xd800) GETUTF16INC(c, p);
#endif
}
#endif
if (caseless)
{
#ifdef SUPPORT_UNICODE
if (utf || ucp)
{
c = UCD_OTHERCASE(c);
#if PCRE2_CODE_UNIT_WIDTH == 8
if (utf)
{
PCRE2_UCHAR buff[6];
(void)PRIV(ord2utf)(c, buff);
SET_BIT(buff[0]);
}
else if (c < 256) SET_BIT(c);
#else
if (c > 0xff) SET_BIT(0xff); else SET_BIT(c);
#endif
}
else
#endif
if (MAX_255(c)) SET_BIT(re->tables[fcc_offset + c]);
}
return p;
}
static void
set_type_bits(pcre2_real_code *re, int cbit_type, unsigned int table_limit)
{
uint32_t c;
for (c = 0; c < table_limit; c++)
re->start_bitmap[c] |= re->tables[c+cbits_offset+cbit_type];
#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
if (table_limit == 32) return;
for (c = 128; c < 256; c++)
{
if ((re->tables[cbits_offset + c/8] & (1u << (c&7))) != 0)
{
PCRE2_UCHAR buff[6];
(void)PRIV(ord2utf)(c, buff);
SET_BIT(buff[0]);
}
}
#endif
}
static void
set_nottype_bits(pcre2_real_code *re, int cbit_type, unsigned int table_limit)
{
uint32_t c;
for (c = 0; c < table_limit; c++)
re->start_bitmap[c] |= (uint8_t)(~(re->tables[c+cbits_offset+cbit_type]));
#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
if (table_limit != 32) for (c = 24; c < 32; c++) re->start_bitmap[c] = 0xff;
#endif
}
#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
static void
study_char_list(PCRE2_SPTR code, uint8_t *start_bitmap,
const uint8_t *char_lists_end)
{
uint32_t type, list_ind;
uint32_t char_list_add = XCL_CHAR_LIST_LOW_16_ADD;
uint32_t range_start = ~(uint32_t)0, range_end = 0;
const uint8_t *next_char;
PCRE2_UCHAR start_buffer[6], end_buffer[6];
PCRE2_UCHAR start, end;
type = (uint32_t)(code[0] << 8) | code[1];
code += 2;
next_char = char_lists_end - (GET(code, 0) << 1);
type &= XCL_TYPE_MASK;
list_ind = 0;
if ((type & XCL_BEGIN_WITH_RANGE) != 0)
range_start = XCL_CHAR_LIST_LOW_16_START;
while (type > 0)
{
uint32_t item_count = type & XCL_ITEM_COUNT_MASK;
if (item_count == XCL_ITEM_COUNT_MASK)
{
if (list_ind <= 1)
{
item_count = *(const uint16_t*)next_char;
next_char += 2;
}
else
{
item_count = *(const uint32_t*)next_char;
next_char += 4;
}
}
while (item_count > 0)
{
if (list_ind <= 1)
{
range_end = *(const uint16_t*)next_char;
next_char += 2;
}
else
{
range_end = *(const uint32_t*)next_char;
next_char += 4;
}
if ((range_end & XCL_CHAR_END) != 0)
{
range_end = char_list_add + (range_end >> XCL_CHAR_SHIFT);
PRIV(ord2utf)(range_end, end_buffer);
end = end_buffer[0];
if (range_start < range_end)
{
PRIV(ord2utf)(range_start, start_buffer);
for (start = start_buffer[0]; start <= end; start++)
start_bitmap[start / 8] |= (1u << (start & 7));
}
else
start_bitmap[end / 8] |= (1u << (end & 7));
range_start = ~(uint32_t)0;
}
else
range_start = char_list_add + (range_end >> XCL_CHAR_SHIFT);
item_count--;
}
list_ind++;
type >>= XCL_TYPE_BIT_LEN;
if (range_start == ~(uint32_t)0)
{
if ((type & XCL_BEGIN_WITH_RANGE) != 0)
{
if (list_ind == 1) range_start = XCL_CHAR_LIST_HIGH_16_START;
else range_start = XCL_CHAR_LIST_LOW_32_START;
}
}
else if ((type & XCL_BEGIN_WITH_RANGE) == 0)
{
PRIV(ord2utf)(range_start, start_buffer);
if (list_ind == 1) range_end = XCL_CHAR_LIST_LOW_16_END;
else range_end = XCL_CHAR_LIST_HIGH_16_END;
PRIV(ord2utf)(range_end, end_buffer);
end = end_buffer[0];
for (start = start_buffer[0]; start <= end; start++)
start_bitmap[start / 8] |= (1u << (start & 7));
range_start = ~(uint32_t)0;
}
if (list_ind == 1) char_list_add = XCL_CHAR_LIST_HIGH_16_ADD;
else char_list_add = XCL_CHAR_LIST_LOW_32_ADD;
}
}
#endif
static int
set_start_bits(pcre2_real_code *re, PCRE2_SPTR code, BOOL utf, BOOL ucp,
int *depthptr)
{
uint32_t c;
int yield = SSB_DONE;
#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
int table_limit = utf? 16:32;
#else
int table_limit = 32;
#endif
*depthptr += 1;
if (*depthptr > 1000) return SSB_TOODEEP;
do
{
BOOL try_next = TRUE;
PCRE2_SPTR tcode = code + 1 + LINK_SIZE;
if (*code == OP_CBRA || *code == OP_SCBRA ||
*code == OP_CBRAPOS || *code == OP_SCBRAPOS) tcode += IMM2_SIZE;
while (try_next)
{
int rc;
PCRE2_SPTR ncode;
const uint8_t *classmap = NULL;
#ifdef SUPPORT_WIDE_CHARS
PCRE2_UCHAR xclassflags;
#endif
switch(*tcode)
{
default:
return SSB_UNKNOWN;
case OP_ACCEPT:
case OP_ASSERT_ACCEPT:
case OP_ALLANY:
case OP_ANY:
case OP_ANYBYTE:
case OP_CIRCM:
case OP_CLOSE:
case OP_COMMIT:
case OP_COMMIT_ARG:
case OP_COND:
case OP_CREF:
case OP_FALSE:
case OP_TRUE:
case OP_DNCREF:
case OP_DNREF:
case OP_DNREFI:
case OP_DNRREF:
case OP_DOLL:
case OP_DOLLM:
case OP_END:
case OP_EOD:
case OP_EODN:
case OP_EXTUNI:
case OP_FAIL:
case OP_MARK:
case OP_NOT:
case OP_NOTEXACT:
case OP_NOTEXACTI:
case OP_NOTI:
case OP_NOTMINPLUS:
case OP_NOTMINPLUSI:
case OP_NOTMINQUERY:
case OP_NOTMINQUERYI:
case OP_NOTMINSTAR:
case OP_NOTMINSTARI:
case OP_NOTMINUPTO:
case OP_NOTMINUPTOI:
case OP_NOTPLUS:
case OP_NOTPLUSI:
case OP_NOTPOSPLUS:
case OP_NOTPOSPLUSI:
case OP_NOTPOSQUERY:
case OP_NOTPOSQUERYI:
case OP_NOTPOSSTAR:
case OP_NOTPOSSTARI:
case OP_NOTPOSUPTO:
case OP_NOTPOSUPTOI:
case OP_NOTPROP:
case OP_NOTQUERY:
case OP_NOTQUERYI:
case OP_NOTSTAR:
case OP_NOTSTARI:
case OP_NOTUPTO:
case OP_NOTUPTOI:
case OP_NOT_HSPACE:
case OP_NOT_VSPACE:
case OP_PRUNE:
case OP_PRUNE_ARG:
case OP_RECURSE:
case OP_REF:
case OP_REFI:
case OP_REVERSE:
case OP_VREVERSE:
case OP_RREF:
case OP_SCOND:
case OP_SET_SOM:
case OP_SKIP:
case OP_SKIP_ARG:
case OP_SOD:
case OP_SOM:
case OP_THEN:
case OP_THEN_ARG:
return SSB_FAIL;
case OP_CIRC:
tcode += PRIV(OP_lengths)[OP_CIRC];
break;
case OP_PROP:
if (tcode[1] != PT_CLIST) return SSB_FAIL;
{
const uint32_t *p = PRIV(ucd_caseless_sets) + tcode[2];
while ((c = *p++) < NOTACHAR)
{
#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
if (utf)
{
PCRE2_UCHAR buff[6];
(void)PRIV(ord2utf)(c, buff);
c = buff[0];
}
#endif
if (c > 0xff) SET_BIT(0xff); else SET_BIT(c);
}
}
try_next = FALSE;
break;
case OP_WORD_BOUNDARY:
case OP_NOT_WORD_BOUNDARY:
case OP_UCP_WORD_BOUNDARY:
case OP_NOT_UCP_WORD_BOUNDARY:
tcode++;
break;
case OP_ASSERT:
case OP_ASSERT_NA:
ncode = tcode + GET(tcode, 1);
while (*ncode == OP_ALT) ncode += GET(ncode, 1);
ncode += 1 + LINK_SIZE;
for (BOOL done = FALSE; !done;)
{
switch (*ncode)
{
case OP_ASSERT:
case OP_ASSERT_NOT:
case OP_ASSERTBACK:
case OP_ASSERTBACK_NOT:
case OP_ASSERT_NA:
case OP_ASSERTBACK_NA:
case OP_ASSERT_SCS:
ncode += GET(ncode, 1);
while (*ncode == OP_ALT) ncode += GET(ncode, 1);
ncode += 1 + LINK_SIZE;
break;
case OP_WORD_BOUNDARY:
case OP_NOT_WORD_BOUNDARY:
case OP_UCP_WORD_BOUNDARY:
case OP_NOT_UCP_WORD_BOUNDARY:
ncode++;
break;
case OP_CALLOUT:
ncode += PRIV(OP_lengths)[OP_CALLOUT];
break;
case OP_CALLOUT_STR:
ncode += GET(ncode, 1 + 2*LINK_SIZE);
break;
default:
done = TRUE;
break;
}
}
switch(*ncode)
{
default:
break;
case OP_PROP:
if (ncode[1] != PT_CLIST) break;
case OP_ANYNL:
case OP_CHAR:
case OP_CHARI:
case OP_EXACT:
case OP_EXACTI:
case OP_HSPACE:
case OP_MINPLUS:
case OP_MINPLUSI:
case OP_PLUS:
case OP_PLUSI:
case OP_POSPLUS:
case OP_POSPLUSI:
case OP_VSPACE:
case OP_DIGIT:
case OP_NOT_DIGIT:
case OP_WORDCHAR:
case OP_NOT_WORDCHAR:
case OP_WHITESPACE:
case OP_NOT_WHITESPACE:
tcode = ncode;
continue;
}
case OP_BRA:
case OP_SBRA:
case OP_CBRA:
case OP_SCBRA:
case OP_BRAPOS:
case OP_SBRAPOS:
case OP_CBRAPOS:
case OP_SCBRAPOS:
case OP_ONCE:
case OP_SCRIPT_RUN:
rc = set_start_bits(re, tcode, utf, ucp, depthptr);
if (rc == SSB_DONE)
{
try_next = FALSE;
}
else if (rc == SSB_CONTINUE)
{
do tcode += GET(tcode, 1); while (*tcode == OP_ALT);
tcode += 1 + LINK_SIZE;
}
else return rc;
break;
case OP_ALT:
yield = SSB_CONTINUE;
try_next = FALSE;
break;
case OP_KET:
case OP_KETRMAX:
case OP_KETRMIN:
case OP_KETRPOS:
return SSB_CONTINUE;
case OP_CALLOUT:
tcode += PRIV(OP_lengths)[OP_CALLOUT];
break;
case OP_CALLOUT_STR:
tcode += GET(tcode, 1 + 2*LINK_SIZE);
break;
case OP_ASSERT_NOT:
case OP_ASSERTBACK:
case OP_ASSERTBACK_NOT:
case OP_ASSERTBACK_NA:
case OP_ASSERT_SCS:
do tcode += GET(tcode, 1); while (*tcode == OP_ALT);
tcode += 1 + LINK_SIZE;
break;
case OP_BRAZERO:
case OP_BRAMINZERO:
case OP_BRAPOSZERO:
rc = set_start_bits(re, ++tcode, utf, ucp, depthptr);
if (rc == SSB_FAIL || rc == SSB_UNKNOWN || rc == SSB_TOODEEP) return rc;
do tcode += GET(tcode,1); while (*tcode == OP_ALT);
tcode += 1 + LINK_SIZE;
break;
case OP_SKIPZERO:
tcode++;
do tcode += GET(tcode,1); while (*tcode == OP_ALT);
tcode += 1 + LINK_SIZE;
break;
case OP_STAR:
case OP_MINSTAR:
case OP_POSSTAR:
case OP_QUERY:
case OP_MINQUERY:
case OP_POSQUERY:
tcode = set_table_bit(re, tcode + 1, FALSE, utf, ucp);
break;
case OP_STARI:
case OP_MINSTARI:
case OP_POSSTARI:
case OP_QUERYI:
case OP_MINQUERYI:
case OP_POSQUERYI:
tcode = set_table_bit(re, tcode + 1, TRUE, utf, ucp);
break;
case OP_UPTO:
case OP_MINUPTO:
case OP_POSUPTO:
tcode = set_table_bit(re, tcode + 1 + IMM2_SIZE, FALSE, utf, ucp);
break;
case OP_UPTOI:
case OP_MINUPTOI:
case OP_POSUPTOI:
tcode = set_table_bit(re, tcode + 1 + IMM2_SIZE, TRUE, utf, ucp);
break;
case OP_EXACT:
tcode += IMM2_SIZE;
case OP_CHAR:
case OP_PLUS:
case OP_MINPLUS:
case OP_POSPLUS:
(void)set_table_bit(re, tcode + 1, FALSE, utf, ucp);
try_next = FALSE;
break;
case OP_EXACTI:
tcode += IMM2_SIZE;
case OP_CHARI:
case OP_PLUSI:
case OP_MINPLUSI:
case OP_POSPLUSI:
(void)set_table_bit(re, tcode + 1, TRUE, utf, ucp);
try_next = FALSE;
break;
case OP_HSPACE:
SET_BIT(CHAR_HT);
SET_BIT(CHAR_SPACE);
#if PCRE2_CODE_UNIT_WIDTH != 8
SET_BIT(0xA0);
SET_BIT(0xFF);
#else
#ifdef SUPPORT_UNICODE
if (utf)
{
SET_BIT(0xC2);
SET_BIT(0xE1);
SET_BIT(0xE2);
SET_BIT(0xE3);
}
else
#endif
{
#ifndef EBCDIC
SET_BIT(0xA0);
#endif
}
#endif
try_next = FALSE;
break;
case OP_ANYNL:
case OP_VSPACE:
SET_BIT(CHAR_LF);
SET_BIT(CHAR_VT);
SET_BIT(CHAR_FF);
SET_BIT(CHAR_CR);
#if PCRE2_CODE_UNIT_WIDTH != 8
SET_BIT(CHAR_NEL);
SET_BIT(0xFF);
#else
#ifdef SUPPORT_UNICODE
if (utf)
{
SET_BIT(0xC2);
SET_BIT(0xE2);
}
else
#endif
{
SET_BIT(CHAR_NEL);
}
#endif
try_next = FALSE;
break;
case OP_NOT_DIGIT:
set_nottype_bits(re, cbit_digit, table_limit);
try_next = FALSE;
break;
case OP_DIGIT:
set_type_bits(re, cbit_digit, table_limit);
try_next = FALSE;
break;
case OP_NOT_WHITESPACE:
set_nottype_bits(re, cbit_space, table_limit);
try_next = FALSE;
break;
case OP_WHITESPACE:
set_type_bits(re, cbit_space, table_limit);
try_next = FALSE;
break;
case OP_NOT_WORDCHAR:
set_nottype_bits(re, cbit_word, table_limit);
try_next = FALSE;
break;
case OP_WORDCHAR:
set_type_bits(re, cbit_word, table_limit);
try_next = FALSE;
break;
case OP_TYPEPLUS:
case OP_TYPEMINPLUS:
case OP_TYPEPOSPLUS:
tcode++;
break;
case OP_TYPEEXACT:
tcode += 1 + IMM2_SIZE;
break;
case OP_TYPEUPTO:
case OP_TYPEMINUPTO:
case OP_TYPEPOSUPTO:
tcode += IMM2_SIZE;
case OP_TYPESTAR:
case OP_TYPEMINSTAR:
case OP_TYPEPOSSTAR:
case OP_TYPEQUERY:
case OP_TYPEMINQUERY:
case OP_TYPEPOSQUERY:
switch(tcode[1])
{
default:
case OP_ANY:
case OP_ALLANY:
return SSB_FAIL;
case OP_HSPACE:
SET_BIT(CHAR_HT);
SET_BIT(CHAR_SPACE);
#if PCRE2_CODE_UNIT_WIDTH != 8
SET_BIT(0xA0);
SET_BIT(0xFF);
#else
#ifdef SUPPORT_UNICODE
if (utf)
{
SET_BIT(0xC2);
SET_BIT(0xE1);
SET_BIT(0xE2);
SET_BIT(0xE3);
}
else
#endif
{
#ifndef EBCDIC
SET_BIT(0xA0);
#endif
}
#endif
break;
case OP_ANYNL:
case OP_VSPACE:
SET_BIT(CHAR_LF);
SET_BIT(CHAR_VT);
SET_BIT(CHAR_FF);
SET_BIT(CHAR_CR);
#if PCRE2_CODE_UNIT_WIDTH != 8
SET_BIT(CHAR_NEL);
SET_BIT(0xFF);
#else
#ifdef SUPPORT_UNICODE
if (utf)
{
SET_BIT(0xC2);
SET_BIT(0xE2);
}
else
#endif
{
SET_BIT(CHAR_NEL);
}
#endif
break;
case OP_NOT_DIGIT:
set_nottype_bits(re, cbit_digit, table_limit);
break;
case OP_DIGIT:
set_type_bits(re, cbit_digit, table_limit);
break;
case OP_NOT_WHITESPACE:
set_nottype_bits(re, cbit_space, table_limit);
break;
case OP_WHITESPACE:
set_type_bits(re, cbit_space, table_limit);
break;
case OP_NOT_WORDCHAR:
set_nottype_bits(re, cbit_word, table_limit);
break;
case OP_WORDCHAR:
set_type_bits(re, cbit_word, table_limit);
break;
}
tcode += 2;
break;
#ifdef SUPPORT_WIDE_CHARS
case OP_ECLASS:
return SSB_FAIL;
#endif
#ifdef SUPPORT_WIDE_CHARS
case OP_XCLASS:
xclassflags = tcode[1 + LINK_SIZE];
if ((xclassflags & XCL_HASPROP) != 0 ||
(xclassflags & (XCL_MAP|XCL_NOT)) == XCL_NOT)
return SSB_FAIL;
classmap = ((xclassflags & XCL_MAP) == 0)? NULL :
(const uint8_t *)(tcode + 1 + LINK_SIZE + 1);
#if PCRE2_CODE_UNIT_WIDTH == 8
if (utf && (xclassflags & XCL_NOT) == 0)
{
PCRE2_UCHAR b, e;
PCRE2_SPTR p = tcode + 1 + LINK_SIZE + 1 + ((classmap == NULL)? 0:32);
tcode += GET(tcode, 1);
if (*p >= XCL_LIST)
{
study_char_list(p, re->start_bitmap,
((const uint8_t *)re + re->code_start));
goto HANDLE_CLASSMAP;
}
for (;;) switch (*p++)
{
case XCL_SINGLE:
b = *p++;
while ((*p & 0xc0) == 0x80) p++;
re->start_bitmap[b/8] |= (1u << (b&7));
break;
case XCL_RANGE:
b = *p++;
while ((*p & 0xc0) == 0x80) p++;
e = *p++;
while ((*p & 0xc0) == 0x80) p++;
for (; b <= e; b++)
re->start_bitmap[b/8] |= (1u << (b&7));
break;
case XCL_END:
goto HANDLE_CLASSMAP;
default:
PCRE2_DEBUG_UNREACHABLE();
return SSB_UNKNOWN;
}
}
#endif
#endif
case OP_NCLASS:
#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
if (utf)
{
re->start_bitmap[24] |= 0xf0;
memset(re->start_bitmap+25, 0xff, 7);
}
#elif PCRE2_CODE_UNIT_WIDTH != 8
SET_BIT(0xFF);
#endif
case OP_CLASS:
if (*tcode == OP_XCLASS) tcode += GET(tcode, 1); else
{
classmap = (const uint8_t *)(++tcode);
tcode += 32 / sizeof(PCRE2_UCHAR);
}
#if defined SUPPORT_WIDE_CHARS && PCRE2_CODE_UNIT_WIDTH == 8
HANDLE_CLASSMAP:
#endif
if (classmap != NULL)
{
#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
if (utf)
{
for (c = 0; c < 16; c++) re->start_bitmap[c] |= classmap[c];
for (c = 128; c < 256; c++)
{
if ((classmap[c/8] & (1u << (c&7))) != 0)
{
int d = (c >> 6) | 0xc0;
re->start_bitmap[d/8] |= (1u << (d&7));
c = (c & 0xc0) + 0x40 - 1;
}
}
}
else
#endif
{
for (c = 0; c < 32; c++) re->start_bitmap[c] |= classmap[c];
}
}
switch (*tcode)
{
case OP_CRSTAR:
case OP_CRMINSTAR:
case OP_CRQUERY:
case OP_CRMINQUERY:
case OP_CRPOSSTAR:
case OP_CRPOSQUERY:
tcode++;
break;
case OP_CRRANGE:
case OP_CRMINRANGE:
case OP_CRPOSRANGE:
if (GET2(tcode, 1) == 0) tcode += 1 + 2 * IMM2_SIZE;
else try_next = FALSE;
break;
default:
try_next = FALSE;
break;
}
break;
}
}
code += GET(code, 1);
}
while (*code == OP_ALT);
return yield;
}
int
PRIV(study)(pcre2_real_code *re)
{
int count = 0;
PCRE2_UCHAR *code;
BOOL utf = (re->overall_options & PCRE2_UTF) != 0;
BOOL ucp = (re->overall_options & PCRE2_UCP) != 0;
code = (PCRE2_UCHAR *)((uint8_t *)re + re->code_start);
if ((re->flags & (PCRE2_FIRSTSET|PCRE2_STARTLINE)) == 0)
{
int depth = 0;
int rc = set_start_bits(re, code, utf, ucp, &depth);
if (rc == SSB_UNKNOWN)
{
PCRE2_DEBUG_UNREACHABLE();
return 1;
}
if (rc == SSB_DONE)
{
int i;
int a = -1;
int b = -1;
uint8_t *p = re->start_bitmap;
uint32_t flags = PCRE2_FIRSTMAPSET;
for (i = 0; i < 256; p++, i += 8)
{
uint8_t x = *p;
if (x != 0)
{
int c;
uint8_t y = x & (~x + 1);
if (y != x) goto DONE;
#if PCRE2_CODE_UNIT_WIDTH != 8
if (i == 248 && x == 0x80) goto DONE;
#endif
c = i;
switch (x)
{
case 1: break;
case 2: c += 1; break; case 4: c += 2; break;
case 8: c += 3; break; case 16: c += 4; break;
case 32: c += 5; break; case 64: c += 6; break;
case 128: c += 7; break;
}
#if PCRE2_CODE_UNIT_WIDTH == 8
if (utf && c > 127) goto DONE;
#endif
if (a < 0) a = c;
else if (b < 0)
{
int d = TABLE_GET((unsigned int)c, re->tables + fcc_offset, c);
#ifdef SUPPORT_UNICODE
if (utf || ucp)
{
if (UCD_CASESET(c) != 0) goto DONE;
if (c > 127) d = UCD_OTHERCASE(c);
}
#endif
if (d != a) goto DONE;
b = c;
}
else goto DONE;
}
}
if (a >= 0) {
if ((re->flags & PCRE2_LASTSET) && (re->last_codeunit == (uint32_t)a || (b >= 0 && re->last_codeunit == (uint32_t)b))) {
re->flags &= ~(PCRE2_LASTSET | PCRE2_LASTCASELESS);
re->last_codeunit = 0;
}
re->first_codeunit = a;
flags = PCRE2_FIRSTSET;
if (b >= 0) flags |= PCRE2_FIRSTCASELESS;
}
DONE:
re->flags |= flags;
}
}
if ((re->flags & (PCRE2_MATCH_EMPTY|PCRE2_HASACCEPT)) == 0 &&
re->top_backref <= MAX_CACHE_BACKREF)
{
int min;
int backref_cache[MAX_CACHE_BACKREF+1];
backref_cache[0] = 0;
min = find_minlength(re, code, code, utf, NULL, &count, backref_cache);
switch(min)
{
case -1:
break;
case -2:
PCRE2_DEBUG_UNREACHABLE();
return 2;
case -3:
PCRE2_DEBUG_UNREACHABLE();
return 3;
default:
re->minlength = (min > UINT16_MAX)? UINT16_MAX : min;
break;
}
}
return 0;
}