/* Copyright (C) 1989, 2000 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: zcontrol.c,v 1.3.6.2.2.1 2003/01/17 00:49:05 giles Exp $ */ /* Control operators */ #include "string_.h" #include "ghost.h" #include "stream.h" #include "oper.h" #include "estack.h" #include "files.h" #include "ipacked.h" #include "iutil.h" #include "store.h" /* Forward references */ private int no_cleanup(P1(i_ctx_t *)); private uint count_exec_stack(P2(i_ctx_t *, bool)); private uint count_to_stopped(P2(i_ctx_t *, long)); private int unmatched_exit(P2(os_ptr, op_proc_t)); /* See the comment in opdef.h for an invariant which allows */ /* more efficient implementation of for, loop, and repeat. */ /* <[test0 body0 ...]> .cond - */ private int cond_continue(P1(i_ctx_t *)); private int zcond(i_ctx_t *i_ctx_p) { os_ptr op = osp; es_ptr ep = esp; /* Push the array on the e-stack and call the continuation. */ if (!r_is_array(op)) return_op_typecheck(op); check_execute(*op); if ((r_size(op) & 1) != 0) return_error(e_rangecheck); if (r_size(op) == 0) return zpop(i_ctx_p); check_estack(3); esp = ep += 3; ref_assign(ep - 2, op); /* the cond body */ make_op_estack(ep - 1, cond_continue); array_get(op, 0L, ep); esfile_check_cache(); pop(1); return o_push_estack; } private int cond_continue(i_ctx_t *i_ctx_p) { os_ptr op = osp; es_ptr ep = esp; int code; /* The top element of the e-stack is the remaining tail of */ /* the cond body. The top element of the o-stack should be */ /* the (boolean) result of the test that is the first element */ /* of the tail. */ check_type(*op, t_boolean); if (op->value.boolval) { /* true */ array_get(ep, 1L, ep); esfile_check_cache(); code = o_pop_estack; } else if (r_size(ep) > 2) { /* false */ const ref_packed *elts = ep->value.packed; check_estack(2); r_dec_size(ep, 2); elts = packed_next(elts); elts = packed_next(elts); ep->value.packed = elts; array_get(ep, 0L, ep + 2); make_op_estack(ep + 1, cond_continue); esp = ep + 2; esfile_check_cache(); code = o_push_estack; } else { /* fall off end of cond */ esp = ep - 1; code = o_pop_estack; } pop(1); /* get rid of the boolean */ return code; } /* exec - */ int zexec(i_ctx_t *i_ctx_p) { os_ptr op = osp; check_op(1); if (!r_has_attr(op, a_executable)) return 0; /* literal object just gets pushed back */ check_estack(1); ++esp; ref_assign(esp, op); esfile_check_cache(); pop(1); return o_push_estack; } /* ... .execn - */ private int zexecn(i_ctx_t *i_ctx_p) { os_ptr op = osp; uint n, i; es_ptr esp_orig; check_int_leu(*op, max_uint - 1); n = (uint) op->value.intval; check_op(n + 1); check_estack(n); esp_orig = esp; for (i = 0; i < n; ++i) { const ref *rp = ref_stack_index(&o_stack, (long)(i + 1)); /* Make sure this object is legal to execute. */ if (ref_type_uses_access(r_type(rp))) { if (!r_has_attr(rp, a_execute) && r_has_attr(rp, a_executable) ) { esp = esp_orig; return_error(e_invalidaccess); } } /* Executable nulls have a special meaning on the e-stack, */ /* so since they are no-ops, don't push them. */ if (!r_has_type_attrs(rp, t_null, a_executable)) { ++esp; ref_assign(esp, rp); } } esfile_check_cache(); pop(n + 1); return o_push_estack; } /* superexec - */ private int end_superexec(P1(i_ctx_t *)); private int zsuperexec(i_ctx_t *i_ctx_p) { os_ptr op = osp; es_ptr ep; check_op(1); if (!r_has_attr(op, a_executable)) return 0; /* literal object just gets pushed back */ check_estack(2); ep = esp += 3; make_mark_estack(ep - 2, es_other, end_superexec); /* error case */ make_op_estack(ep - 1, end_superexec); /* normal case */ ref_assign(ep, op); esfile_check_cache(); pop(1); i_ctx_p->in_superexec++; return o_push_estack; } private int end_superexec(i_ctx_t *i_ctx_p) { i_ctx_p->in_superexec--; return 0; } /* .runandhide */ /* before executing , is been removed from */ /* the operand stack and placed on the execstack with attributes */ /* changed to 'noaccess'. */ /* After execution, the array will be placed on the top of the */ /* operand stack (on top of any elemetns pushed by */ /* for both the normal case and for the error case. */ private int end_runandhide(P1(i_ctx_t *)); private int err_end_runandhide(P1(i_ctx_t *)); private int zrunandhide(i_ctx_t *i_ctx_p) { os_ptr op = osp; es_ptr ep; uint size; int code; check_op(2); if (!r_is_array(op - 1)) return_op_typecheck(op); if (!r_has_attr(op, a_executable)) return 0; /* literal object just gets pushed back */ check_estack(5); ep = esp += 5; make_mark_estack(ep - 4, es_other, err_end_runandhide); /* error case */ make_op_estack(ep - 1, end_runandhide); /* normal case */ ref_assign(ep, op); /* Store the object we are hiding and it's current tas.type_attrs */ /* on the exec stack then change to 'noaccess' */ make_int(ep - 3, (int)op[-1].tas.type_attrs); ref_assign(ep - 2, op - 1); r_clear_attrs(ep - 2, a_all); /* replace the array with a special kind of mark that has a_read access */ esfile_check_cache(); pop(2); return o_push_estack; } private int runandhide_restore_hidden(i_ctx_t *i_ctx_p, ref *obj, ref *attrs) { os_ptr op = osp; push(1); /* restore the hidden_object and its type_attrs */ ref_assign(op, obj); r_clear_attrs(op, a_all); r_set_attrs(op, attrs->value.intval); return 0; } /* - %end_runandhide hiddenobject */ private int end_runandhide(i_ctx_t *i_ctx_p) { int code; if ((code = runandhide_restore_hidden(i_ctx_p, esp, esp - 1)) < 0) return code; esp -= 2; /* pop the hidden value and its atributes */ return o_pop_estack; } /* restore hidden object for error returns */ private int err_end_runandhide(i_ctx_t *i_ctx_p) { int code; if ((code = runandhide_restore_hidden(i_ctx_p, esp + 3, esp + 2)) < 0) return code; return 0; } /* if - */ int zif(i_ctx_t *i_ctx_p) { os_ptr op = osp; check_type(op[-1], t_boolean); check_proc(*op); if (op[-1].value.boolval) { check_estack(1); ++esp; ref_assign(esp, op); esfile_check_cache(); } pop(2); return o_push_estack; } /* ifelse - */ int zifelse(i_ctx_t *i_ctx_p) { os_ptr op = osp; check_type(op[-2], t_boolean); check_proc(op[-1]); check_proc(*op); check_estack(1); ++esp; if (op[-2].value.boolval) { ref_assign(esp, op - 1); } else { ref_assign(esp, op); } esfile_check_cache(); pop(3); return o_push_estack; } /* for - */ private int for_pos_int_continue(P1(i_ctx_t *)), for_neg_int_continue(P1(i_ctx_t *)), for_real_continue(P1(i_ctx_t *)); int zfor(i_ctx_t *i_ctx_p) { os_ptr op = osp; register es_ptr ep; check_estack(7); ep = esp + 6; check_proc(*op); /* Push a mark, the control variable, the initial value, */ /* the increment, the limit, and the procedure, */ /* and invoke the continuation operator. */ if (r_has_type(op - 3, t_integer) && r_has_type(op - 2, t_integer) ) { make_int(ep - 4, op[-3].value.intval); make_int(ep - 3, op[-2].value.intval); switch (r_type(op - 1)) { case t_integer: make_int(ep - 2, op[-1].value.intval); break; case t_real: make_int(ep - 2, (long)op[-1].value.realval); break; default: return_op_typecheck(op - 1); } if (ep[-3].value.intval >= 0) make_op_estack(ep, for_pos_int_continue); else make_op_estack(ep, for_neg_int_continue); } else { float params[3]; int code; if ((code = float_params(op - 1, 3, params)) < 0) return code; make_real(ep - 4, params[0]); make_real(ep - 3, params[1]); make_real(ep - 2, params[2]); make_op_estack(ep, for_real_continue); } make_mark_estack(ep - 5, es_for, no_cleanup); ref_assign(ep - 1, op); esp = ep; pop(4); return o_push_estack; } /* Continuation operators for for, separate for positive integer, */ /* negative integer, and real. */ /* Execution stack contains mark, control variable, increment, */ /* limit, and procedure (procedure is topmost.) */ /* Continuation operator for positive integers. */ private int for_pos_int_continue(i_ctx_t *i_ctx_p) { os_ptr op = osp; register es_ptr ep = esp; long var = ep[-3].value.intval; if (var > ep[-1].value.intval) { esp -= 5; /* pop everything */ return o_pop_estack; } push(1); make_int(op, var); ep[-3].value.intval = var + ep[-2].value.intval; ref_assign_inline(ep + 2, ep); /* saved proc */ esp = ep + 2; return o_push_estack; } /* Continuation operator for negative integers. */ private int for_neg_int_continue(i_ctx_t *i_ctx_p) { os_ptr op = osp; register es_ptr ep = esp; long var = ep[-3].value.intval; if (var < ep[-1].value.intval) { esp -= 5; /* pop everything */ return o_pop_estack; } push(1); make_int(op, var); ep[-3].value.intval = var + ep[-2].value.intval; ref_assign(ep + 2, ep); /* saved proc */ esp = ep + 2; return o_push_estack; } /* Continuation operator for reals. */ private int for_real_continue(i_ctx_t *i_ctx_p) { os_ptr op = osp; es_ptr ep = esp; float var = ep[-3].value.realval; float incr = ep[-2].value.realval; if (incr >= 0 ? (var > ep[-1].value.realval) : (var < ep[-1].value.realval) ) { esp -= 5; /* pop everything */ return o_pop_estack; } push(1); ref_assign(op, ep - 3); ep[-3].value.realval = var + incr; esp = ep + 2; ref_assign(ep + 2, ep); /* saved proc */ return o_push_estack; } /* Here we provide an internal variant of 'for' that enumerates the */ /* values 0, 1/N, 2/N, ..., 1 precisely. The arguments must be */ /* the integers 0, 1, and N. We need this for */ /* loading caches such as the transfer function cache. */ private int for_fraction_continue(P1(i_ctx_t *)); int zfor_fraction(i_ctx_t *i_ctx_p) { int code = zfor(i_ctx_p); if (code < 0) return code; /* shouldn't ever happen! */ make_op_estack(esp, for_fraction_continue); return code; } /* Continuation procedure */ private int for_fraction_continue(i_ctx_t *i_ctx_p) { register es_ptr ep = esp; int code = for_pos_int_continue(i_ctx_p); if (code != o_push_estack) return code; /* We must use osp instead of op here, because */ /* for_pos_int_continue pushes a value on the o-stack. */ make_real(osp, (float)osp->value.intval / ep[-1].value.intval); return code; } /* repeat - */ private int repeat_continue(P1(i_ctx_t *)); private int zrepeat(i_ctx_t *i_ctx_p) { os_ptr op = osp; check_type(op[-1], t_integer); check_proc(*op); if (op[-1].value.intval < 0) return_error(e_rangecheck); check_estack(5); /* Push a mark, the count, and the procedure, and invoke */ /* the continuation operator. */ push_mark_estack(es_for, no_cleanup); *++esp = op[-1]; *++esp = *op; make_op_estack(esp + 1, repeat_continue); pop(2); return repeat_continue(i_ctx_p); } /* Continuation operator for repeat */ private int repeat_continue(i_ctx_t *i_ctx_p) { es_ptr ep = esp; /* saved proc */ if (--(ep[-1].value.intval) >= 0) { /* continue */ esp += 2; ref_assign(esp, ep); return o_push_estack; } else { /* done */ esp -= 3; /* pop mark, count, proc */ return o_pop_estack; } } /* loop */ private int loop_continue(P1(i_ctx_t *)); private int zloop(i_ctx_t *i_ctx_p) { os_ptr op = osp; check_proc(*op); check_estack(4); /* Push a mark and the procedure, and invoke */ /* the continuation operator. */ push_mark_estack(es_for, no_cleanup); *++esp = *op; make_op_estack(esp + 1, loop_continue); pop(1); return loop_continue(i_ctx_p); } /* Continuation operator for loop */ private int loop_continue(i_ctx_t *i_ctx_p) { register es_ptr ep = esp; /* saved proc */ ref_assign(ep + 2, ep); esp = ep + 2; return o_push_estack; } /* - exit - */ private int zexit(i_ctx_t *i_ctx_p) { os_ptr op = osp; ref_stack_enum_t rsenum; uint scanned = 0; ref_stack_enum_begin(&rsenum, &e_stack); do { uint used = rsenum.size; es_ptr ep = rsenum.ptr + used - 1; uint count = used; for (; count; count--, ep--) if (r_is_estack_mark(ep)) switch (estack_mark_index(ep)) { case es_for: pop_estack(i_ctx_p, scanned + (used - count + 1)); return o_pop_estack; case es_stopped: return_error(e_invalidexit); /* not a loop */ } scanned += used; } while (ref_stack_enum_next(&rsenum)); /* No mark, quit. (per Adobe documentation) */ push(2); return unmatched_exit(op, zexit); } /* * .stopped pushes the following on the e-stack: * - A mark with type = es_stopped and procedure = no_cleanup. * - The result to be pushed on a normal return. * - The signal mask for .stop. * - The procedure %stopped_push, to handle the normal return case. */ /* In the normal (no-error) case, pop the mask from the e-stack, */ /* and move the result to the o-stack. */ private int stopped_push(i_ctx_t *i_ctx_p) { os_ptr op = osp; push(1); *op = esp[-1]; esp -= 3; return o_pop_estack; } /* - stop - */ /* Equivalent to true 1 .stop. */ /* This is implemented in C because if were a pseudo-operator, */ /* the stacks would get restored in case of an error. */ private int zstop(i_ctx_t *i_ctx_p) { os_ptr op = osp; uint count = count_to_stopped(i_ctx_p, 1L); if (count) { /* * If there are any t_oparrays on the e-stack, they will pop * any new items from the o-stack. Wait to push the 'true' * until we have run all the unwind procedures. */ check_ostack(2); pop_estack(i_ctx_p, count); op = osp; push(1); make_true(op); return o_pop_estack; } /* No mark, quit. (per Adobe documentation) */ push(2); return unmatched_exit(op, zstop); } /* .stop - */ private int zzstop(i_ctx_t *i_ctx_p) { os_ptr op = osp; uint count; check_type(*op, t_integer); count = count_to_stopped(i_ctx_p, op->value.intval); if (count) { /* * If there are any t_oparrays on the e-stack, they will pop * any new items from the o-stack. Wait to push the result * until we have run all the unwind procedures. */ ref save_result; check_op(2); save_result = op[-1]; pop(2); pop_estack(i_ctx_p, count); op = osp; push(1); *op = save_result; return o_pop_estack; } /* No mark, quit. (per Adobe documentation) */ return unmatched_exit(op, zzstop); } /* stopped */ /* Equivalent to false 1 .stopped. */ /* This is implemented in C because if were a pseudo-operator, */ /* the stacks would get restored in case of an error. */ private int zstopped(i_ctx_t *i_ctx_p) { os_ptr op = osp; check_op(1); /* Mark the execution stack, and push the default result */ /* in case control returns normally. */ check_estack(5); push_mark_estack(es_stopped, no_cleanup); ++esp; make_false(esp); /* save the result */ ++esp; make_int(esp, 1); /* save the signal mask */ push_op_estack(stopped_push); *++esp = *op; /* execute the operand */ esfile_check_cache(); pop(1); return o_push_estack; } /* .stopped */ private int zzstopped(i_ctx_t *i_ctx_p) { os_ptr op = osp; check_type(*op, t_integer); check_op(3); /* Mark the execution stack, and push the default result */ /* in case control returns normally. */ check_estack(5); push_mark_estack(es_stopped, no_cleanup); *++esp = op[-1]; /* save the result */ *++esp = *op; /* save the signal mask */ push_op_estack(stopped_push); *++esp = op[-2]; /* execute the operand */ esfile_check_cache(); pop(3); return o_push_estack; } /* .instopped false */ /* .instopped true */ private int zinstopped(i_ctx_t *i_ctx_p) { os_ptr op = osp; uint count; check_type(*op, t_integer); count = count_to_stopped(i_ctx_p, op->value.intval); if (count) { push(1); op[-1] = *ref_stack_index(&e_stack, count - 2); /* default result */ make_true(op); } else make_false(op); return 0; } /* .countexecstack */ /* - countexecstack */ /* countexecstack is an operator solely for the sake of the Genoa tests. */ private int zcountexecstack(i_ctx_t *i_ctx_p) { os_ptr op = osp; push(1); make_int(op, count_exec_stack(i_ctx_p, false)); return 0; } private int zcountexecstack1(i_ctx_t *i_ctx_p) { os_ptr op = osp; check_type(*op, t_boolean); make_int(op, count_exec_stack(i_ctx_p, op->value.boolval)); return 0; } /* .execstack */ /* execstack */ /* execstack is an operator solely for the sake of the Genoa tests. */ private int execstack_continue(P1(i_ctx_t *)); private int execstack2_continue(P1(i_ctx_t *)); private int push_execstack(i_ctx_t *i_ctx_p, os_ptr op1, bool include_marks, op_proc_t cont) { uint size; /* * We can't do this directly, because the interpreter * might have cached some state. To force the interpreter * to update the stored state, we push a continuation on * the exec stack; the continuation is executed immediately, * and does the actual transfer. */ uint depth; check_write_type(*op1, t_array); size = r_size(op1); depth = count_exec_stack(i_ctx_p, include_marks); if (depth > size) return_error(e_rangecheck); { int code = ref_stack_store_check(&e_stack, op1, size, 0); if (code < 0) return code; } check_estack(1); r_set_size(op1, depth); push_op_estack(cont); return o_push_estack; } private int zexecstack(i_ctx_t *i_ctx_p) { os_ptr op = osp; return push_execstack(i_ctx_p, op, false, execstack_continue); } private int zexecstack2(i_ctx_t *i_ctx_p) { os_ptr op = osp; check_type(*op, t_boolean); return push_execstack(i_ctx_p, op - 1, op->value.boolval, execstack2_continue); } /* Continuation operator to do the actual transfer. */ /* r_size(op1) was set just above. */ private int do_execstack(i_ctx_t *i_ctx_p, bool include_marks, os_ptr op1) { os_ptr op = osp; ref *arefs = op1->value.refs; uint asize = r_size(op1); uint i; ref *rq; /* * Copy elements from the stack to the array, * optionally skipping executable nulls. * Clear the executable bit in any internal operators, and * convert t_structs and t_astructs (which can only appear * in connection with stack marks, which means that they will * probably be freed when unwinding) to something harmless. */ for (i = 0, rq = arefs + asize; rq != arefs; ++i) { const ref *rp = ref_stack_index(&e_stack, (long)i); if (r_has_type_attrs(rp, t_null, a_executable) && !include_marks) continue; --rq; ref_assign_old(op1, rq, rp, "execstack"); switch (r_type(rq)) { case t_operator: { uint opidx = op_index(rq); if (opidx == 0 || op_def_is_internal(op_index_def(opidx))) r_clear_attrs(rq, a_executable); break; } case t_struct: case t_astruct: { const char *tname = gs_struct_type_name_string( gs_object_type(imemory, rq->value.pstruct)); make_const_string(rq, a_readonly | avm_foreign, strlen(tname), (const byte *)tname); break; } default: ; } } pop(op - op1); return 0; } private int execstack_continue(i_ctx_t *i_ctx_p) { os_ptr op = osp; return do_execstack(i_ctx_p, false, op); } private int execstack2_continue(i_ctx_t *i_ctx_p) { os_ptr op = osp; return do_execstack(i_ctx_p, op->value.boolval, op - 1); } /* - .needinput - */ private int zneedinput(i_ctx_t *i_ctx_p) { return e_NeedInput; /* interpreter will exit to caller */ } /* .quit - */ private int zquit(i_ctx_t *i_ctx_p) { os_ptr op = osp; check_op(2); check_type(*op, t_integer); return_error(e_Quit); /* Interpreter will do the exit */ } /* - currentfile */ private ref *zget_current_file(P1(i_ctx_t *)); private int zcurrentfile(i_ctx_t *i_ctx_p) { os_ptr op = osp; ref *fp; push(1); /* Check the cache first */ if (esfile != 0) { #ifdef DEBUG /* Check that esfile is valid. */ ref *efp = zget_current_file(i_ctx_p); if (esfile != efp) { lprintf2("currentfile: esfile=0x%lx, efp=0x%lx\n", (ulong) esfile, (ulong) efp); ref_assign(op, efp); } else #endif ref_assign(op, esfile); } else if ((fp = zget_current_file(i_ctx_p)) == 0) { /* Return an invalid file object. */ /* This doesn't make a lot of sense to me, */ /* but it's what the PostScript manual specifies. */ make_invalid_file(op); } else { ref_assign(op, fp); esfile_set_cache(fp); } /* Make the returned value literal. */ r_clear_attrs(op, a_executable); return 0; } /* Get the current file from which the interpreter is reading. */ private ref * zget_current_file(i_ctx_t *i_ctx_p) { ref_stack_enum_t rsenum; ref_stack_enum_begin(&rsenum, &e_stack); do { uint count = rsenum.size; es_ptr ep = rsenum.ptr + count - 1; for (; count; count--, ep--) if (r_has_type_attrs(ep, t_file, a_executable)) return ep; } while (ref_stack_enum_next(&rsenum)); return 0; } /* ------ Initialization procedure ------ */ /* We need to split the table because of the 16-element limit. */ const op_def zcontrol1_op_defs[] = { {"1.cond", zcond}, {"0countexecstack", zcountexecstack}, {"1.countexecstack", zcountexecstack1}, {"0currentfile", zcurrentfile}, {"1exec", zexec}, {"1.execn", zexecn}, {"1execstack", zexecstack}, {"2.execstack", zexecstack2}, {"0exit", zexit}, {"2if", zif}, {"3ifelse", zifelse}, {"0.instopped", zinstopped}, {"0.needinput", zneedinput}, op_def_end(0) }; const op_def zcontrol2_op_defs[] = { {"4for", zfor}, {"1loop", zloop}, {"2.quit", zquit}, {"2repeat", zrepeat}, {"0stop", zstop}, {"1.stop", zzstop}, {"1stopped", zstopped}, {"2.stopped", zzstopped}, op_def_end(0) }; const op_def zcontrol3_op_defs[] = { /* Internal operators */ {"1%cond_continue", cond_continue}, {"1%execstack_continue", execstack_continue}, {"2%execstack2_continue", execstack2_continue}, {"0%for_pos_int_continue", for_pos_int_continue}, {"0%for_neg_int_continue", for_neg_int_continue}, {"0%for_real_continue", for_real_continue}, {"4%for_fraction", zfor_fraction}, {"0%for_fraction_continue", for_fraction_continue}, {"0%loop_continue", loop_continue}, {"0%repeat_continue", repeat_continue}, {"0%stopped_push", stopped_push}, {"1superexec", zsuperexec}, {"0%end_superexec", end_superexec}, {"2.runandhide", zrunandhide}, {"0%end_runandhide", end_runandhide}, op_def_end(0) }; /* ------ Internal routines ------ */ /* Vacuous cleanup routine */ private int no_cleanup(i_ctx_t *i_ctx_p) { return 0; } /* * Count the number of elements on the exec stack, with or without * the normally invisible elements (*op is a Boolean that indicates this). */ private uint count_exec_stack(i_ctx_t *i_ctx_p, bool include_marks) { uint count = ref_stack_count(&e_stack); if (!include_marks) { uint i; for (i = count; i--;) if (r_has_type_attrs(ref_stack_index(&e_stack, (long)i), t_null, a_executable)) --count; } return count; } /* * Count the number of elements down to and including the first 'stopped' * mark on the e-stack with a given mask. Return 0 if there is no 'stopped' * mark. */ private uint count_to_stopped(i_ctx_t *i_ctx_p, long mask) { ref_stack_enum_t rsenum; uint scanned = 0; ref_stack_enum_begin(&rsenum, &e_stack); do { uint used = rsenum.size; es_ptr ep = rsenum.ptr + used - 1; uint count = used; for (; count; count--, ep--) if (r_is_estack_mark(ep) && estack_mark_index(ep) == es_stopped && (ep[2].value.intval & mask) != 0 ) return scanned + (used - count + 1); scanned += used; } while (ref_stack_enum_next(&rsenum)); return 0; } /* * Pop the e-stack, executing cleanup procedures as needed. * We could make this more efficient using ref_stack_enum_*, * but it isn't used enough to make this worthwhile. */ void pop_estack(i_ctx_t *i_ctx_p, uint count) { uint idx = 0; uint popped = 0; esfile_clear_cache(); for (; idx < count; idx++) { ref *ep = ref_stack_index(&e_stack, idx - popped); if (r_is_estack_mark(ep)) { ref_stack_pop(&e_stack, idx + 1 - popped); popped = idx + 1; (*real_opproc(ep)) (i_ctx_p); } } ref_stack_pop(&e_stack, count - popped); } /* * Execute a quit in the case of an exit or stop with no appropriate * enclosing control scope (loop or stopped). The caller has already * ensured two free slots on the top of the o-stack. */ private int unmatched_exit(os_ptr op, op_proc_t opproc) { make_oper(op - 1, 0, opproc); make_int(op, e_invalidexit); return_error(e_Quit); }