const re_match_context_t *mctx, int idx);
static void update_regs (re_dfa_t *dfa, regmatch_t *pmatch, int cur_node,
int cur_idx, int nmatch);
-static int proceed_next_node (const regex_t *preg, regmatch_t *regs,
+static int proceed_next_node (const regex_t *preg, int nregs, regmatch_t *regs,
const re_match_context_t *mctx,
- int *pidx, int node, re_node_set *eps_via_nodes);
+ int *pidx, int node, re_node_set *eps_via_nodes,
+ struct re_fail_stack_t *fs);
+static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs,
+ int str_idx, int *dests, int nregs,
+ regmatch_t *regs,
+ re_node_set *eps_via_nodes);
+static int pop_fail_stack (struct re_fail_stack_t *fs, int *pidx, int nregs,
+ regmatch_t *regs, re_node_set *eps_via_nodes);
static reg_errcode_t set_regs (const regex_t *preg,
const re_match_context_t *mctx,
- size_t nmatch, regmatch_t *pmatch, int last);
+ size_t nmatch, regmatch_t *pmatch,
+ int fl_backtrack);
#ifdef RE_ENABLE_I18N
static int sift_states_iter_mb (const regex_t *preg,
const re_match_context_t *mctx,
static reg_errcode_t sub_epsilon_src_nodes (re_dfa_t *dfa, int node,
re_node_set *dest_nodes,
const re_node_set *and_nodes);
+static int check_dst_limits (re_dfa_t *dfa, re_node_set *limits,
+ re_match_context_t *mctx, int dst_node,
+ int dst_idx, int src_node, int src_idx);
+static int check_dst_limits_calc_pos (re_dfa_t *dfa, re_match_context_t *mctx,
+ int limit, re_node_set *eclosures,
+ int subexp_idx, int node, int str_idx);
static reg_errcode_t check_subexp_limits (re_dfa_t *dfa,
re_node_set *dest_nodes,
const re_node_set *candidates,
re_free (mctx.state_log);
mctx.state_log = sifted_states;
}
- err = set_regs (preg, &mctx, nmatch, pmatch, halt_node);
+ mctx.last_node = halt_node;
+ err = set_regs (preg, &mctx, nmatch, pmatch,
+ dfa->has_plural_match && dfa->nbackref > 0);
if (BE (err != REG_NOERROR, 0))
return err;
}
of errors. */
static int
-proceed_next_node (preg, regs, mctx, pidx, node, eps_via_nodes)
+proceed_next_node (preg, nregs, regs, mctx, pidx, node, eps_via_nodes, fs)
const regex_t *preg;
regmatch_t *regs;
const re_match_context_t *mctx;
- int *pidx, node;
+ int nregs, *pidx, node;
re_node_set *eps_via_nodes;
+ struct re_fail_stack_t *fs;
{
re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
int i, err, dest_node, cur_entity;
? dfa->nodes[node].opr.ctx_info->entity : node);
if (IS_EPSILON_NODE (dfa->nodes[node].type))
{
- int dest_entity = INT_MAX;
+ int ndest, dest_nodes[2], dest_entities[2];
err = re_node_set_insert (eps_via_nodes, node);
if (BE (err < 0, 0))
return -1;
- for (i = 0; i < mctx->state_log[*pidx]->nodes.nelem; ++i)
+ /* Pick up valid destinations. */
+ for (ndest = 0, i = 0; i < mctx->state_log[*pidx]->nodes.nelem; ++i)
{
- int candidate, candidate_entity;
- candidate = mctx->state_log[*pidx]->nodes.elems[i];
- candidate_entity = ((dfa->nodes[candidate].type == OP_CONTEXT_NODE)
- ? dfa->nodes[candidate].opr.ctx_info->entity
- : candidate);
- if (!re_node_set_contains (dfa->edests + node, candidate))
- if (candidate == candidate_entity
- || !re_node_set_contains (dfa->edests + node, candidate_entity))
- continue;
-
- /* In order to avoid infinite loop like "(a*)*". */
- if (cur_entity > candidate_entity
- && re_node_set_contains (eps_via_nodes, candidate))
+ int candidate = mctx->state_log[*pidx]->nodes.elems[i];
+ int entity;
+ entity = ((dfa->nodes[candidate].type == OP_CONTEXT_NODE)
+ ? dfa->nodes[candidate].opr.ctx_info->entity : candidate);
+ if (!re_node_set_contains (dfa->edests + node, entity))
continue;
-
- if (dest_entity > candidate_entity)
- {
- dest_node = candidate;
- dest_entity = candidate_entity;
- }
+ dest_nodes[0] = (ndest == 0) ? candidate : dest_nodes[0];
+ dest_entities[0] = (ndest == 0) ? entity : dest_entities[0];
+ dest_nodes[1] = (ndest == 1) ? candidate : dest_nodes[1];
+ dest_entities[1] = (ndest == 1) ? entity : dest_entities[1];
+ ++ndest;
}
-#ifdef DEBUG
- assert (dest_node != -1);
-#endif
- return dest_node;
+ if (ndest <= 1)
+ return ndest == 0 ? -1 : (ndest == 1 ? dest_nodes[0] : 0);
+ if (dest_entities[0] > dest_entities[1])
+ {
+ int swap_work = dest_nodes[0];
+ dest_nodes[0] = dest_nodes[1];
+ dest_nodes[1] = swap_work;
+ }
+ /* In order to avoid infinite loop like "(a*)*". */
+ if (re_node_set_contains (eps_via_nodes, dest_nodes[0]))
+ return dest_nodes[1];
+ if (fs != NULL)
+ push_fail_stack (fs, *pidx, dest_nodes, nregs, regs, eps_via_nodes);
+ return dest_nodes[0];
}
else
{
{
int subexp_idx = dfa->nodes[entity].opr.idx;
naccepted = regs[subexp_idx].rm_eo - regs[subexp_idx].rm_so;
+ if (fs != NULL)
+ {
+ if (regs[subexp_idx].rm_so == -1 || regs[subexp_idx].rm_eo == -1)
+ return -1;
+ else if (naccepted)
+ {
+ char *buf = re_string_get_buffer (mctx->input);
+ if (strncmp (buf + regs[subexp_idx].rm_so, buf + *pidx,
+ naccepted) != 0)
+ return -1;
+ }
+ }
if (naccepted == 0)
{
err = re_node_set_insert (eps_via_nodes, node);
if (BE (err < 0, 0))
- return -1;
+ return -2;
dest_node = dfa->nexts[node];
if (re_node_set_contains (&mctx->state_log[*pidx]->nodes,
dest_node))
{
dest_node = dfa->nexts[node];
*pidx = (naccepted == 0) ? *pidx + 1 : *pidx + naccepted;
-#ifdef DEBUG
- assert (mctx->state_log[*pidx] != NULL);
-#endif
+ if (fs && (*pidx > mctx->match_last || mctx->state_log[*pidx] == NULL
+ || !re_node_set_contains (&mctx->state_log[*pidx]->nodes,
+ dest_node)))
+ return -1;
re_node_set_empty (eps_via_nodes);
return dest_node;
}
}
- /* Must not reach here. */
-#ifdef DEBUG
- assert (0);
-#endif
- return 0;
+ return -1;
+}
+
+static reg_errcode_t
+push_fail_stack (fs, str_idx, dests, nregs, regs, eps_via_nodes)
+ struct re_fail_stack_t *fs;
+ int str_idx, *dests, nregs;
+ regmatch_t *regs;
+ re_node_set *eps_via_nodes;
+{
+ reg_errcode_t err;
+ int num = fs->num++;
+ if (fs->num == fs->alloc)
+ {
+ fs->alloc *= 2;
+ fs->stack = realloc (fs->stack, (sizeof (struct re_fail_stack_ent_t)
+ * fs->alloc));
+ if (fs->stack == NULL)
+ return REG_ESPACE;
+ }
+ fs->stack[num].idx = str_idx;
+ fs->stack[num].node = dests[1];
+ fs->stack[num].regs = re_malloc (regmatch_t, nregs);
+ memcpy (fs->stack[num].regs, regs, sizeof (regmatch_t) * nregs);
+ err = re_node_set_init_copy (&fs->stack[num].eps_via_nodes, eps_via_nodes);
+ return err;
+}
+
+static int
+pop_fail_stack (fs, pidx, nregs, regs, eps_via_nodes)
+ struct re_fail_stack_t *fs;
+ int *pidx, nregs;
+ regmatch_t *regs;
+ re_node_set *eps_via_nodes;
+{
+ int num = --fs->num;
+ assert (num >= 0);
+ *pidx = fs->stack[num].idx;
+ memcpy (regs, fs->stack[num].regs, sizeof (regmatch_t) * nregs);
+ re_node_set_free (eps_via_nodes);
+ *eps_via_nodes = fs->stack[num].eps_via_nodes;
+ return fs->stack[num].node;
}
/* Set the positions where the subexpressions are starts/ends to registers
pmatch[i].rm_so == pmatch[i].rm_eo == -1 (i > 1). */
static reg_errcode_t
-set_regs (preg, mctx, nmatch, pmatch, last_node)
- const regex_t *preg;
- const re_match_context_t *mctx;
- size_t nmatch;
- regmatch_t *pmatch;
- int last_node;
+set_regs (preg, mctx, nmatch, pmatch, fl_backtrack)
+ const regex_t *preg;
+ const re_match_context_t *mctx;
+ size_t nmatch;
+ regmatch_t *pmatch;
+ int fl_backtrack;
{
re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
int idx, cur_node, real_nmatch;
re_node_set eps_via_nodes;
+ struct re_fail_stack_t *fs;
+ struct re_fail_stack_t fs_body = {0, 2, NULL};
#ifdef DEBUG
assert (nmatch > 1);
assert (mctx->state_log != NULL);
#endif
+ if (fl_backtrack)
+ {
+ fs = &fs_body;
+ fs->stack = re_malloc (struct re_fail_stack_ent_t, fs->alloc);
+ }
+ else
+ fs = NULL;
cur_node = dfa->init_node;
real_nmatch = (nmatch <= preg->re_nsub) ? nmatch : preg->re_nsub + 1;
re_node_set_init_empty (&eps_via_nodes);
for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;)
{
update_regs (dfa, pmatch, cur_node, idx, real_nmatch);
- if (idx == pmatch[0].rm_eo && cur_node == last_node)
- break;
+ if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node)
+ {
+ int reg_idx;
+ if (fs)
+ {
+ for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+ if (pmatch[reg_idx].rm_so > -1 && pmatch[reg_idx].rm_eo == -1)
+ break;
+ if (reg_idx == nmatch)
+ return REG_NOERROR;
+ cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
+ &eps_via_nodes);
+ }
+ else
+ return REG_NOERROR;
+ }
/* Proceed to next node. */
- cur_node = proceed_next_node (preg, pmatch, mctx, &idx, cur_node,
- &eps_via_nodes);
+ cur_node = proceed_next_node (preg, nmatch, pmatch, mctx, &idx, cur_node,
+ &eps_via_nodes, fs);
+
if (BE (cur_node < 0, 0))
- return REG_ESPACE;
+ {
+ if (cur_node == -2)
+ return REG_ESPACE;
+ if (fs)
+ cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
+ &eps_via_nodes);
+ else
+ return REG_NOMATCH;
+ }
}
re_node_set_free (&eps_via_nodes);
return REG_NOERROR;
if (naccepted == 0)
continue;
+ if (sctx->limits.nelem)
+ {
+ int to_idx = str_idx + naccepted;
+ if (check_dst_limits (dfa, &sctx->limits, mctx,
+ dfa->nexts[prev_node], to_idx,
+ prev_node, str_idx))
+ continue;
+ }
ret = re_node_set_insert (&cur_dest, prev_node);
if (BE (ret == -1, 0))
return err;
return REG_NOERROR;
}
+static int
+check_dst_limits (dfa, limits, mctx, dst_node, dst_idx, src_node, src_idx)
+ re_dfa_t *dfa;
+ re_node_set *limits;
+ re_match_context_t *mctx;
+ int dst_node, dst_idx, src_node, src_idx;
+{
+ int lim_idx, src_pos, dst_pos;
+
+ for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
+ {
+ int bkref, subexp_idx/*, node_idx, cls_node*/;
+ struct re_backref_cache_entry *ent;
+ ent = mctx->bkref_ents + limits->elems[lim_idx];
+ bkref = (dfa->nodes[ent->node].type == OP_CONTEXT_NODE
+ ? dfa->nodes[ent->node].opr.ctx_info->entity : ent->node);
+ subexp_idx = dfa->nodes[bkref].opr.idx - 1;
+
+ dst_pos = check_dst_limits_calc_pos (dfa, mctx, limits->elems[lim_idx],
+ dfa->eclosures + dst_node,
+ subexp_idx, dst_node, dst_idx);
+ src_pos = check_dst_limits_calc_pos (dfa, mctx, limits->elems[lim_idx],
+ dfa->eclosures + src_node,
+ subexp_idx, src_node, src_idx);
+
+ /* In case of:
+ <src> <dst> ( <subexp> )
+ ( <subexp> ) <src> <dst>
+ ( <subexp1> <src> <subexp2> <dst> <subexp3> ) */
+ if (src_pos == dst_pos)
+ continue; /* This is unrelated limitation. */
+ else
+ return 1;
+ }
+ return 0;
+}
+
+static int
+check_dst_limits_calc_pos (dfa, mctx, limit, eclosures, subexp_idx, node,
+ str_idx)
+ re_dfa_t *dfa;
+ re_match_context_t *mctx;
+ re_node_set *eclosures;
+ int limit, subexp_idx, node, str_idx;
+{
+ struct re_backref_cache_entry *lim = mctx->bkref_ents + limit;
+ int pos = (str_idx < lim->subexp_from ? -1
+ : (lim->subexp_to < str_idx ? 1 : 0));
+ if (pos == 0
+ && (str_idx == lim->subexp_from || str_idx == lim->subexp_to))
+ {
+ int node_idx;
+ for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx)
+ {
+ int node = eclosures->elems[node_idx];
+ int entity = node;
+ re_token_type_t type= dfa->nodes[node].type;
+ if (type == OP_CONTEXT_NODE)
+ {
+ entity = dfa->nodes[node].opr.ctx_info->entity;
+ type = dfa->nodes[entity].type;
+ }
+ if (type == OP_BACK_REF)
+ {
+ int bi;
+ for (bi = 0; bi < mctx->nbkref_ents; ++bi)
+ {
+ struct re_backref_cache_entry *ent = mctx->bkref_ents + bi;
+ if (ent->node == node && ent->subexp_from == ent->subexp_to
+ && ent->str_idx == str_idx)
+ {
+ int cpos, dst;
+ dst = dfa->nexts[node];
+ cpos = check_dst_limits_calc_pos (dfa, mctx, limit,
+ dfa->eclosures + dst,
+ subexp_idx, dst,
+ str_idx);
+ if ((str_idx == lim->subexp_from && cpos == -1)
+ || (str_idx == lim->subexp_to && cpos == 0))
+ return cpos;
+ }
+ }
+ }
+ if (type == OP_OPEN_SUBEXP && subexp_idx == dfa->nodes[node].opr.idx
+ && str_idx == lim->subexp_from)
+ {
+ pos = -1;
+ break;
+ }
+ if (type == OP_CLOSE_SUBEXP && subexp_idx == dfa->nodes[node].opr.idx
+ && str_idx == lim->subexp_to)
+ break;
+ }
+ if (node_idx == eclosures->nelem && str_idx == lim->subexp_to)
+ pos = 1;
+ }
+ return pos;
+}
+
/* Check the limitations of sub expressions LIMITS, and remove the nodes
which are against limitations from DEST_NODES. */
re_sift_context_t local_sctx;
int node_idx, node;
const re_node_set *candidates;
+ re_dfastate_t **lim_states = NULL;
candidates = ((mctx->state_log[str_idx] == NULL) ? &empty_set
: &mctx->state_log[str_idx]->nodes);
local_sctx.sifted_states = NULL; /* Mark that it hasn't been initialized. */
if (type == OP_CLOSE_SUBEXP
&& sctx->check_subexp == dfa->nodes[node].opr.idx + 1)
{
+ re_dfastate_t *cur_state;
/* Found the bottom of the subexpression, then search for the
top of it. */
if (local_sctx.sifted_states == NULL)
return err;
}
local_sctx.check_subexp = -sctx->check_subexp;
+ local_sctx.limited_states = sctx->limited_states;
local_sctx.last_node = node;
local_sctx.last_str_idx = local_sctx.cls_subexp_idx = str_idx;
+ cur_state = local_sctx.sifted_states[str_idx];
err = sift_states_backward (preg, mctx, &local_sctx);
+ local_sctx.sifted_states[str_idx] = cur_state;
if (BE (err != REG_NOERROR, 0))
return err;
/* There must not 2 same node in a node set. */
buf = (char *) re_string_get_buffer (mctx->input);
if (strncmp (buf + str_idx, buf + bkref_str_idx, subexp_len) != 0)
break;
+
+ if (sctx->limits.nelem && str_idx > 0)
+ {
+ re_dfastate_t *cur_state = sctx->sifted_states[str_idx];
+ if (lim_states == NULL)
+ {
+ lim_states = re_malloc (re_dfastate_t *, str_idx + 1);
+ }
+ if (local_sctx.sifted_states == NULL)
+ {
+ /* It hasn't been initialized yet, initialize it now. */
+ local_sctx = *sctx;
+ if (BE (lim_states == NULL, 0))
+ return REG_ESPACE;
+ err = re_node_set_init_copy (&local_sctx.limits,
+ &sctx->limits);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ local_sctx.check_subexp = 0;
+ local_sctx.last_node = node;
+ local_sctx.last_str_idx = str_idx;
+ local_sctx.limited_states = lim_states;
+ memset (lim_states, '\0',
+ sizeof (re_dfastate_t*) * (str_idx + 1));
+ err = sift_states_backward (preg, mctx, &local_sctx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ if (local_sctx.sifted_states[0] == NULL
+ && local_sctx.limited_states[0] == NULL)
+ {
+ sctx->sifted_states[str_idx] = cur_state;
+ break;
+ }
+ sctx->sifted_states[str_idx] = cur_state;
+ }
/* Successfully matched, add a new cache entry. */
dest_str_idx = bkref_str_idx + subexp_len;
err = match_ctx_add_entry (mctx, sctx->cur_bkref, bkref_str_idx,
if (local_sctx.sifted_states != NULL)
re_node_set_free (&local_sctx.limits);
+ if (lim_states != NULL)
+ re_free (lim_states);
return REG_NOERROR;
}
continue;
}
+ if (check_dst_limits (dfa, &sctx->limits, mctx, node,
+ str_idx, dfa->nexts[node], to_idx))
+ continue;
if (sctx->check_subexp == dfa->nodes[entity].opr.idx)
{
char *buf;
}
else
{
+ re_dfastate_t *cur_state;
entry->flag = 0;
for (disabled_idx = enabled_idx + 1;
disabled_idx < mctx->nbkref_ents; ++disabled_idx)
{
struct re_backref_cache_entry *entry2;
- entry2 = mctx->bkref_ents + enabled_idx;
+ entry2 = mctx->bkref_ents + disabled_idx;
if (entry2->node != node || entry2->str_idx != str_idx)
continue;
entry2->flag = 1;
if (local_sctx.sifted_states == NULL)
{
local_sctx = *sctx;
- local_sctx.sifted_states = re_malloc (re_dfastate_t *,
- str_idx + 1);
- if (BE (local_sctx.sifted_states == NULL, 0))
- return REG_ESPACE;
err = re_node_set_init_copy (&local_sctx.limits,
&sctx->limits);
if (BE (err != REG_NOERROR, 0))
err = re_node_set_insert (&local_sctx.limits, enabled_idx);
if (BE (err < 0, 0))
return REG_ESPACE;
+ cur_state = local_sctx.sifted_states[str_idx];
err = sift_states_backward (preg, mctx, &local_sctx);
if (BE (err != REG_NOERROR, 0))
return err;
if (BE (err != REG_NOERROR, 0))
return err;
}
+ local_sctx.sifted_states[str_idx] = cur_state;
re_node_set_remove_at (&local_sctx.limits,
local_sctx.limits.nelem - 1);
entry->flag = 1;
}
if (local_sctx.sifted_states != NULL)
{
- free (local_sctx.sifted_states);
re_node_set_free (&local_sctx.limits);
}