/**********************************************************************
regexec.c - Oniguruma (regular expression library)
Copyright (C) 2002-2004 K.Kosako (kosako@sofnec.co.jp)
**********************************************************************/
#include "regint.h"
static void
region_list_clear(OnigRegion** list)
{
int i;
if (IS_NOT_NULL(list)) {
for (i = 1; i <= ONIG_MAX_CAPTURE_HISTORY_GROUP; i++) {
if (IS_NOT_NULL(list[i])) {
xfree(list[i]);
list[i] = (OnigRegion* )0;
}
}
}
}
static void
region_list_free(OnigRegion* r)
{
if (IS_NOT_NULL(r->list)) {
region_list_clear(r->list);
xfree(r->list);
r->list = (OnigRegion** )0;
}
}
static OnigRegion**
region_list_new()
{
int i;
OnigRegion** list;
list = (OnigRegion** )xmalloc(sizeof(OnigRegion*)
* (ONIG_MAX_CAPTURE_HISTORY_GROUP + 1));
CHECK_NULL_RETURN(list);
for (i = 0; i <= ONIG_MAX_CAPTURE_HISTORY_GROUP; i++) {
list[i] = (OnigRegion* )0;
}
return list;
}
extern void
onig_region_clear(OnigRegion* region)
{
int i;
for (i = 0; i < region->num_regs; i++) {
region->beg[i] = region->end[i] = ONIG_REGION_NOTPOS;
}
region_list_clear(region->list);
}
extern int
onig_region_resize(OnigRegion* region, int n)
{
int i;
region->num_regs = n;
if (n < ONIG_NREGION)
n = ONIG_NREGION;
if (region->allocated == 0) {
region->beg = (int* )xmalloc(n * sizeof(int));
region->end = (int* )xmalloc(n * sizeof(int));
if (region->beg == 0 || region->end == 0)
return ONIGERR_MEMORY;
region->allocated = n;
}
else if (region->allocated < n) {
region->beg = (int* )xrealloc(region->beg, n * sizeof(int));
region->end = (int* )xrealloc(region->end, n * sizeof(int));
if (region->beg == 0 || region->end == 0)
return ONIGERR_MEMORY;
region->allocated = n;
}
for (i = 0; i < region->num_regs; i++) {
region->beg[i] = region->end[i] = ONIG_REGION_NOTPOS;
}
if (IS_NOT_NULL(region->list))
region_list_clear(region->list);
return 0;
}
static int
region_ensure_size(OnigRegion* region, int n)
{
int i, new_size;
if (region->allocated >= n)
return 0;
new_size = region->allocated;
if (new_size == 0)
new_size = ONIG_NREGION;
while (new_size < n)
new_size *= 2;
if (region->allocated == 0) {
region->beg = (int* )xmalloc(new_size * sizeof(int));
region->end = (int* )xmalloc(new_size * sizeof(int));
if (region->beg == 0 || region->end == 0)
return ONIGERR_MEMORY;
region->allocated = new_size;
}
else if (region->allocated < new_size) {
region->beg = (int* )xrealloc(region->beg, new_size * sizeof(int));
region->end = (int* )xrealloc(region->end, new_size * sizeof(int));
if (region->beg == 0 || region->end == 0)
return ONIGERR_MEMORY;
region->allocated = new_size;
}
for (i = region->num_regs; i < n; i++) {
region->beg[i] = region->end[i] = ONIG_REGION_NOTPOS;
}
return 0;
}
static int
region_list_add_entry(OnigRegion* region, int group, int start, int end)
{
int r, pos;
OnigRegion** list;
if (group > ONIG_MAX_CAPTURE_HISTORY_GROUP)
return ONIGERR_GROUP_NUMBER_OVER_FOR_CAPTURE_HISTORY;
if (IS_NULL(region->list)) {
region->list = region_list_new();
CHECK_NULL_RETURN_VAL(region->list, ONIGERR_MEMORY);
}
list = region->list;
if (IS_NULL(list[group])) {
list[group] = onig_region_new();
CHECK_NULL_RETURN_VAL(list[group], ONIGERR_MEMORY);
}
r = region_ensure_size(list[group], list[group]->num_regs + 1);
if (r != 0) return r;
pos = list[group]->num_regs;
list[group]->beg[pos] = start;
list[group]->end[pos] = end;
list[group]->num_regs++;
return 0;
}
static void
onig_region_init(OnigRegion* region)
{
region->num_regs = 0;
region->allocated = 0;
region->beg = (int* )0;
region->end = (int* )0;
region->list = (OnigRegion** )0;
}
extern OnigRegion*
onig_region_new()
{
OnigRegion* r;
r = (OnigRegion* )xmalloc(sizeof(OnigRegion));
onig_region_init(r);
return r;
}
extern void
onig_region_free(OnigRegion* r, int free_self)
{
if (r) {
if (r->allocated > 0) {
if (r->beg) xfree(r->beg);
if (r->end) xfree(r->end);
r->allocated = 0;
}
region_list_free(r);
if (free_self) xfree(r);
}
}
extern void
onig_region_copy(OnigRegion* to, OnigRegion* from)
{
#define RREGC_SIZE (sizeof(int) * from->num_regs)
int i;
if (to == from) return;
if (to->allocated == 0) {
if (from->num_regs > 0) {
to->beg = (int* )xmalloc(RREGC_SIZE);
to->end = (int* )xmalloc(RREGC_SIZE);
to->allocated = from->num_regs;
}
}
else if (to->allocated < from->num_regs) {
to->beg = (int* )xrealloc(to->beg, RREGC_SIZE);
to->end = (int* )xrealloc(to->end, RREGC_SIZE);
to->allocated = from->num_regs;
}
for (i = 0; i < from->num_regs; i++) {
to->beg[i] = from->beg[i];
to->end[i] = from->end[i];
}
to->num_regs = from->num_regs;
if (IS_NOT_NULL(from->list)) {
if (IS_NULL(to->list)) {
to->list = region_list_new();
}
for (i = 1; i <= ONIG_MAX_CAPTURE_HISTORY_GROUP; i++) {
if (IS_NOT_NULL(from->list[i])) {
if (IS_NULL(to->list[i]))
to->list[i] = onig_region_new();
onig_region_copy(to->list[i], from->list[i]);
}
else {
if (IS_NOT_NULL(to->list[i])) {
xfree(to->list[i]);
to->list[i] = (OnigRegion* )0;
}
}
}
}
else
region_list_free(to);
}
/** stack **/
#define INVALID_STACK_INDEX -1
typedef int StackIndex;
typedef struct _StackType {
unsigned int type;
union {
struct {
UChar *pcode; /* byte code position */
UChar *pstr; /* string position */
UChar *pstr_prev; /* previous char position of pstr */
} state;
struct {
int count; /* for OP_REPEAT_INC, OP_REPEAT_INC_NG */
UChar *pcode; /* byte code position (head of repeated target) */
int num; /* repeat id */
} repeat;
struct {
StackIndex si; /* index of stack */
} repeat_inc;
struct {
int num; /* memory num */
UChar *pstr; /* start/end position */
/* Following information is setted, if this stack type is MEM-START */
StackIndex start; /* prev. info (for backtrack "(...)*" ) */
StackIndex end; /* prev. info (for backtrack "(...)*" ) */
} mem;
struct {
int num; /* null check id */
UChar *pstr; /* start position */
} null_check;
#ifdef USE_SUBEXP_CALL
struct {
UChar *ret_addr; /* byte code position */
int num; /* null check id */
UChar *pstr; /* string position */
} call_frame;
#endif
} u;
} StackType;
/* stack type */
/* used by normal-POP */
#define STK_ALT 0x0001
#define STK_LOOK_BEHIND_NOT 0x0003
#define STK_POS_NOT 0x0005
/* avoided by normal-POP, but value should be small */
#define STK_NULL_CHECK_START 0x0100
/* handled by normal-POP */
#define STK_MEM_START 0x0200
#define STK_MEM_END 0x0300
#define STK_REPEAT_INC 0x0400
/* avoided by normal-POP */
#define STK_POS 0x0500 /* used when POP-POS */
#define STK_STOP_BT 0x0600 /* mark for "(?>...)" */
#define STK_REPEAT 0x0700
#define STK_CALL_FRAME 0x0800
#define STK_RETURN 0x0900
#define STK_MEM_END_MARK 0x0a00
#define STK_VOID 0x0b00 /* for fill a blank */
#define STK_NULL_CHECK_END 0x0c00 /* for recursive call */
/* stack type check mask */
#define STK_MASK_POP_USED 0x00ff
#define IS_TO_VOID_TARGET(stk) \
(((stk)->type & STK_MASK_POP_USED) || (stk)->type == STK_NULL_CHECK_START)
typedef struct {
void* stack_p;
int stack_n;
OnigOptionType options;
OnigRegion* region;
UChar* start; /* search start position (for \G: BEGIN_POSITION) */
} MatchArg;
#define MATCH_ARG_INIT(msa, arg_option, arg_region, arg_start) do {\
(msa).stack_p = (void* )0;\
(msa).options = (arg_option);\
(msa).region = (arg_region);\
(msa).start = (arg_start);\
} while (0)
#define MATCH_ARG_FREE(msa) if ((msa).stack_p) xfree((msa).stack_p)
#define STACK_INIT(alloc_addr, ptr_num, stack_num) do {\
if (msa->stack_p) {\
alloc_addr = (char* )xalloca(sizeof(char*) * (ptr_num));\
stk_alloc = (StackType* )(msa->stack_p);\
stk_base = stk_alloc;\
stk = stk_base;\
stk_end = stk_base + msa->stack_n;\
}\
else {\
alloc_addr = (char* )xalloca(sizeof(char*) * (ptr_num)\
+ sizeof(StackType) * (stack_num));\
stk_alloc = (StackType* )(alloc_addr + sizeof(char*) * (ptr_num));\
stk_base = stk_alloc;\
stk = stk_base;\
stk_end = stk_base + (stack_num);\
}\
} while(0)
#define STACK_SAVE do{\
if (stk_base != stk_alloc) {\
msa->stack_p = stk_base;\
msa->stack_n = stk_end - stk_base;\
};\
} while(0)
static unsigned int MatchStackLimitSize = DEFAULT_MATCH_STACK_LIMIT_SIZE;
extern unsigned int
onig_get_match_stack_limit_size(void)
{
return MatchStackLimitSize;
}
extern int
onig_set_match_stack_limit_size(unsigned int size)
{
MatchStackLimitSize = size;
return 0;
}
static int
stack_double(StackType** arg_stk_base, StackType** arg_stk_end,
StackType** arg_stk, StackType* stk_alloc, MatchArg* msa)
{
unsigned int n;
StackType *x, *stk_base, *stk_end, *stk;
stk_base = *arg_stk_base;
stk_end = *arg_stk_end;
stk = *arg_stk;
n = stk_end - stk_base;
if (stk_base == stk_alloc && IS_NULL(msa->stack_p)) {
x = (StackType* )xmalloc(sizeof(StackType) * n * 2);
if (IS_NULL(x)) {
STACK_SAVE;
return ONIGERR_MEMORY;
}
xmemcpy(x, stk_base, n * sizeof(StackType));
n *= 2;
}
else {
n *= 2;
if (MatchStackLimitSize != 0 && n > MatchStackLimitSize) {
if ((unsigned int )(stk_end - stk_base) == MatchStackLimitSize)
return ONIGERR_MATCH_STACK_LIMIT_OVER;
else
n = MatchStackLimitSize;
}
x = (StackType* )xrealloc(stk_base, sizeof(StackType) * n);
if (IS_NULL(x)) {
STACK_SAVE;
return ONIGERR_MEMORY;
}
}
*arg_stk = x + (stk - stk_base);
*arg_stk_base = x;
*arg_stk_end = x + n;
return 0;
}
#define STACK_ENSURE(n) do {\
if (stk_end - stk < (n)) {\
int r = stack_double(&stk_base, &stk_end, &stk, stk_alloc, msa);\
if (r != 0) { STACK_SAVE; return r; } \
}\
} while(0)
#define STACK_AT(index) (stk_base + (index))
#define GET_STACK_INDEX(stk) ((stk) - stk_base)
#define STACK_PUSH(stack_type,pat,s,sprev) do {\
STACK_ENSURE(1);\
stk->type = (stack_type);\
stk->u.state.pcode = (pat);\
stk->u.state.pstr = (s);\
stk->u.state.pstr_prev = (sprev);\
STACK_INC;\
} while(0)
#define STACK_PUSH_ENSURED(stack_type,pat) do {\
stk->type = (stack_type);\
stk->u.state.pcode = (pat);\
STACK_INC;\
} while(0)
#define STACK_PUSH_TYPE(stack_type) do {\
STACK_ENSURE(1);\
stk->type = (stack_type);\
STACK_INC;\
} while(0)
#define STACK_PUSH_ALT(pat,s,sprev) STACK_PUSH(STK_ALT,pat,s,sprev)
#define STACK_PUSH_POS(s,sprev) STACK_PUSH(STK_POS,NULL_UCHARP,s,sprev)
#define STACK_PUSH_POS_NOT(pat,s,sprev) STACK_PUSH(STK_POS_NOT,pat,s,sprev)
#define STACK_PUSH_STOP_BT STACK_PUSH_TYPE(STK_STOP_BT)
#define STACK_PUSH_LOOK_BEHIND_NOT(pat,s,sprev) \
STACK_PUSH(STK_LOOK_BEHIND_NOT,pat,s,sprev)
#define STACK_PUSH_REPEAT(id, pat) do {\
STACK_ENSURE(1);\
stk->type = STK_REPEAT;\
stk->u.repeat.num = (id);\
stk->u.repeat.pcode = (pat);\
stk->u.repeat.count = 0;\
STACK_INC;\
} while(0)
#define STACK_PUSH_REPEAT_INC(sindex) do {\
STACK_ENSURE(1);\
stk->type = STK_REPEAT_INC;\
stk->u.repeat_inc.si = (sindex);\
STACK_INC;\
} while(0)
#define STACK_PUSH_MEM_START(mnum, s) do {\
STACK_ENSURE(1);\
stk->type = STK_MEM_START;\
stk->u.mem.num = (mnum);\
stk->u.mem.pstr = (s);\
stk->u.mem.start = mem_start_stk[mnum];\
stk->u.mem.end = mem_end_stk[mnum];\
mem_start_stk[mnum] = GET_STACK_INDEX(stk);\
mem_end_stk[mnum] = INVALID_STACK_INDEX;\
STACK_INC;\
} while(0)
#define STACK_PUSH_MEM_END(mnum, s) do {\
STACK_ENSURE(1);\
stk->type = STK_MEM_END;\
stk->u.mem.num = (mnum);\
stk->u.mem.pstr = (s);\
stk->u.mem.start = mem_start_stk[mnum];\
stk->u.mem.end = mem_end_stk[mnum];\
mem_end_stk[mnum] = GET_STACK_INDEX(stk);\
STACK_INC;\
} while(0)
#define STACK_PUSH_MEM_END_MARK(mnum) do {\
STACK_ENSURE(1);\
stk->type = STK_MEM_END_MARK;\
stk->u.mem.num = (mnum);\
STACK_INC;\
} while(0)
#define STACK_GET_MEM_START(mnum, k) do {\
int level = 0;\
k = stk;\
while (k > stk_base) {\
k--;\
if ((k->type == STK_MEM_END_MARK || k->type == STK_MEM_END) \
&& k->u.mem.num == (mnum)) {\
level++;\
}\
else if (k->type == STK_MEM_START && k->u.mem.num == (mnum)) {\
if (level == 0) break;\
level--;\
}\
}\
} while (0)
#define STACK_GET_MEM_RANGE(k, mnum, start, end) do {\
int level = 0;\
while (k < stk) {\
if (k->type == STK_MEM_START && k->u.mem.num == (mnum)) {\
if (level == 0) (start) = k->u.mem.pstr;\
level++;\
}\
else if (k->type == STK_MEM_END && k->u.mem.num == (mnum)) {\
level--;\
if (level == 0) {\
(end) = k->u.mem.pstr;\
break;\
}\
}\
k++;\
}\
} while (0)
#define STACK_PUSH_NULL_CHECK_START(cnum, s) do {\
STACK_ENSURE(1);\
stk->type = STK_NULL_CHECK_START;\
stk->u.null_check.num = (cnum);\
stk->u.null_check.pstr = (s);\
STACK_INC;\
} while(0)
#define STACK_PUSH_NULL_CHECK_END(cnum) do {\
STACK_ENSURE(1);\
stk->type = STK_NULL_CHECK_END;\
stk->u.null_check.num = (cnum);\
STACK_INC;\
} while(0)
#define STACK_PUSH_CALL_FRAME(pat) do {\
STACK_ENSURE(1);\
stk->type = STK_CALL_FRAME;\
stk->u.call_frame.ret_addr = (pat);\
STACK_INC;\
} while(0)
#define STACK_PUSH_RETURN do {\
STACK_ENSURE(1);\
stk->type = STK_RETURN;\
STACK_INC;\
} while(0)
#ifdef ONIG_DEBUG
#define STACK_BASE_CHECK(p) \
if ((p) < stk_base) goto stack_error;
#else
#define STACK_BASE_CHECK(p)
#endif
#define STACK_POP_ONE do {\
stk--;\
STACK_BASE_CHECK(stk); \
} while(0)
#define STACK_POP do {\
switch (pop_level) {\
case STACK_POP_LEVEL_FREE:\
while (1) {\
stk--;\
STACK_BASE_CHECK(stk); \
if ((stk->type & STK_MASK_POP_USED) != 0) break;\
}\
break;\
case STACK_POP_LEVEL_MEM_START:\
while (1) {\
stk--;\
STACK_BASE_CHECK(stk); \
if ((stk->type & STK_MASK_POP_USED) != 0) break;\
else if (stk->type == STK_MEM_START) {\
mem_start_stk[stk->u.mem.num] = stk->u.mem.start;\
mem_end_stk[stk->u.mem.num] = stk->u.mem.end;\
}\
}\
break;\
default:\
while (1) {\
stk--;\
STACK_BASE_CHECK(stk); \
if ((stk->type & STK_MASK_POP_USED) != 0) break;\
else if (stk->type == STK_MEM_START) {\
mem_start_stk[stk->u.mem.num] = stk->u.mem.start;\
mem_end_stk[stk->u.mem.num] = stk->u.mem.end;\
}\
else if (stk->type == STK_REPEAT_INC) {\
STACK_AT(stk->u.repeat_inc.si)->u.repeat.count--;\
}\
else if (stk->type == STK_MEM_END) {\
mem_start_stk[stk->u.mem.num] = stk->u.mem.start;\
mem_end_stk[stk->u.mem.num] = stk->u.mem.end;\
}\
}\
break;\
}\
} while(0)
#define STACK_POP_TIL_POS_NOT do {\
while (1) {\
stk--;\
STACK_BASE_CHECK(stk); \
if (stk->type == STK_POS_NOT) break;\
else if (stk->type == STK_MEM_START) {\
mem_start_stk[stk->u.mem.num] = stk->u.mem.start;\
mem_end_stk[stk->u.mem.num] = stk->u.mem.end;\
}\
else if (stk->type == STK_REPEAT_INC) {\
STACK_AT(stk->u.repeat_inc.si)->u.repeat.count--;\
}\
else if (stk->type == STK_MEM_END) {\
mem_start_stk[stk->u.mem.num] = stk->u.mem.start;\
mem_end_stk[stk->u.mem.num] = stk->u.mem.end;\
}\
}\
} while(0)
#define STACK_POP_TIL_LOOK_BEHIND_NOT do {\
while (1) {\
stk--;\
STACK_BASE_CHECK(stk); \
if (stk->type == STK_LOOK_BEHIND_NOT) break;\
else if (stk->type == STK_MEM_START) {\
mem_start_stk[stk->u.mem.num] = stk->u.mem.start;\
mem_end_stk[stk->u.mem.num] = stk->u.mem.end;\
}\
else if (stk->type == STK_REPEAT_INC) {\
STACK_AT(stk->u.repeat_inc.si)->u.repeat.count--;\
}\
else if (stk->type == STK_MEM_END) {\
mem_start_stk[stk->u.mem.num] = stk->u.mem.start;\
mem_end_stk[stk->u.mem.num] = stk->u.mem.end;\
}\
}\
} while(0)
#define STACK_POS_END(k) do {\
k = stk;\
while (1) {\
k--;\
STACK_BASE_CHECK(k); \
if (IS_TO_VOID_TARGET(k)) {\
k->type = STK_VOID;\
}\
else if (k->type == STK_POS) {\
k->type = STK_VOID;\
break;\
}\
}\
} while(0)
#define STACK_STOP_BT_END do {\
StackType *k = stk;\
while (1) {\
k--;\
STACK_BASE_CHECK(k); \
if (IS_TO_VOID_TARGET(k)) {\
k->type = STK_VOID;\
}\
else if (k->type == STK_STOP_BT) {\
k->type = STK_VOID;\
break;\
}\
}\
} while(0)
#define STACK_NULL_CHECK(isnull,id,s) do {\
StackType* k = stk;\
while (1) {\
k--;\
STACK_BASE_CHECK(k); \
if (k->type == STK_NULL_CHECK_START) {\
if (k->u.null_check.num == (id)) {\
(isnull) = (k->u.null_check.pstr == (s));\
break;\
}\
}\
}\
} while(0)
#define STACK_NULL_CHECK_REC(isnull,id,s) do {\
int level = 0;\
StackType* k = stk;\
while (1) {\
k--;\
STACK_BASE_CHECK(k); \
if (k->type == STK_NULL_CHECK_START) {\
if (k->u.null_check.num == (id)) {\
if (level == 0) {\
(isnull) = (k->u.null_check.pstr == (s));\
break;\
}\
else level--;\
}\
}\
else if (k->type == STK_NULL_CHECK_END) {\
level++;\
}\
}\
} while(0)
#define STACK_NULL_CHECK_MEMST(isnull,id,s,reg) do {\
StackType* k = stk;\
while (1) {\
k--;\
STACK_BASE_CHECK(k); \
if (k->type == STK_NULL_CHECK_START) {\
if (k->u.null_check.num == (id)) {\
if (k->u.null_check.pstr != (s)) {\
(isnull) = 0;\
break;\
}\
else {\
UChar* endp;\
(isnull) = 1;\
while (k < stk) {\
if (k->type == STK_MEM_START) {\
if (k->u.mem.end == INVALID_STACK_INDEX) {\
(isnull) = 0; break;\
}\
if (BIT_STATUS_AT(reg->bt_mem_end, k->u.mem.num))\
endp = STACK_AT(k->u.mem.end)->u.mem.pstr;\
else\
endp = (UChar* )k->u.mem.end;\
if (STACK_AT(k->u.mem.start)->u.mem.pstr != endp) {\
(isnull) = 0; break;\
}\
else if (endp != s) {\
(isnull) = -1; /* empty, but position changed */ \
}\
}\
k++;\
}\
break;\
}\
}\
}\
}\
} while(0)
#define STACK_NULL_CHECK_MEMST_REC(isnull,id,s,reg) do {\
int level = 0;\
StackType* k = stk;\
while (1) {\
k--;\
STACK_BASE_CHECK(k); \
if (k->type == STK_NULL_CHECK_START) {\
if (k->u.null_check.num == (id)) {\
if (level == 0) {\
if (k->u.null_check.pstr != (s)) {\
(isnull) = 0;\
break;\
}\
else {\
UChar* endp;\
(isnull) = 1;\
while (k < stk) {\
if (k->type == STK_MEM_START) {\
if (k->u.mem.end == INVALID_STACK_INDEX) {\
(isnull) = 0; break;\
}\
if (BIT_STATUS_AT(reg->bt_mem_end, k->u.mem.num))\
endp = STACK_AT(k->u.mem.end)->u.mem.pstr;\
else\
endp = (UChar* )k->u.mem.end;\
if (STACK_AT(k->u.mem.start)->u.mem.pstr != endp) {\
(isnull) = 0; break;\
}\
else if (endp != s) {\
(isnull) = -1; /* empty, but position changed */ \
}\
}\
k++;\
}\
break;\
}\
}\
else {\
level--;\
}\
}\
}\
else if (k->type == STK_NULL_CHECK_END) {\
if (k->u.null_check.num == (id)) level++;\
}\
}\
} while(0)
#define STACK_GET_REPEAT(id, k) do {\
int level = 0;\
k = stk;\
while (1) {\
k--;\
STACK_BASE_CHECK(k); \
if (k->type == STK_REPEAT) {\
if (level == 0) {\
if (k->u.repeat.num == (id)) {\
break;\
}\
}\
}\
else if (k->type == STK_CALL_FRAME) level--;\
else if (k->type == STK_RETURN) level++;\
}\
} while (0)
#define STACK_RETURN(addr) do {\
int level = 0;\
StackType* k = stk;\
while (1) {\
k--;\
STACK_BASE_CHECK(k); \
if (k->type == STK_CALL_FRAME) {\
if (level == 0) {\
(addr) = k->u.call_frame.ret_addr;\
break;\
}\
else level--;\
}\
else if (k->type == STK_RETURN)\
level++;\
}\
} while(0)
|