/* Common logic for [i][d]transform */ static int common_transform(i_ctx_t *i_ctx_p, int (*ptproc)(gs_state *, double, double, gs_point *), int (*matproc)(double, double, const gs_matrix *, gs_point *)) { os_ptr op = osp; double opxy[2]; gs_point pt; int code; /* Optimize for the non-matrix case */ switch (r_type(op)) { case t_real: opxy[1] = op->value.realval; break; case t_integer: opxy[1] = (double)op->value.intval; break; case t_array: /* might be a matrix */ case t_shortarray: case t_mixedarray: { gs_matrix mat; gs_matrix *pmat = &mat; if ((code = read_matrix(imemory, op, pmat)) < 0 || (code = num_params(op - 1, 2, opxy)) < 0 || (code = (*matproc) (opxy[0], opxy[1], pmat, &pt)) < 0 ) { /* Might be a stack underflow. */ check_op(3); return code; } op--; pop(1); goto out; } default: return_op_typecheck(op); } switch (r_type(op - 1)) { case t_real: opxy[0] = (op - 1)->value.realval; break; case t_integer: opxy[0] = (double)(op - 1)->value.intval; break; default: return_op_typecheck(op - 1); } if ((code = (*ptproc) (igs, opxy[0], opxy[1], &pt)) < 0) return code; out: make_real(op - 1, pt.x); make_real(op, pt.y); return 0; }
/* <num1> <num2> mul <product> */ int zmul(i_ctx_t *i_ctx_p) { os_ptr op = osp; switch (r_type(op)) { default: return_op_typecheck(op); case t_real: switch (r_type(op - 1)) { default: return_op_typecheck(op - 1); case t_real: op[-1].value.realval *= op->value.realval; break; case t_integer: make_real(op - 1, (double)op[-1].value.intval * op->value.realval); } break; case t_integer: switch (r_type(op - 1)) { default: return_op_typecheck(op - 1); case t_real: op[-1].value.realval *= (double)op->value.intval; break; case t_integer: { int int1 = op[-1].value.intval; int int2 = op->value.intval; uint abs1 = (uint)(int1 >= 0 ? int1 : -int1); uint abs2 = (uint)(int2 >= 0 ? int2 : -int2); float fprod; if ((abs1 > MAX_HALF_INTVAL || abs2 > MAX_HALF_INTVAL) && /* At least one of the operands is very large. */ /* Check for integer overflow. */ abs1 != 0 && abs2 > MAX_INTVAL / abs1 && /* Check for the boundary case */ (fprod = (float)int1 * int2, (int1 * int2 != MIN_INTVAL || fprod != (float)MIN_INTVAL)) ) make_real(op - 1, fprod); else op[-1].value.intval = int1 * int2; } } } pop(1); return 0; }
/* Return 0 if OK, error code if not. */ int read_matrix(const gs_memory_t *mem, const ref * op, gs_matrix * pmat) { int code; ref values[6]; const ref *pvalues; switch (r_type(op)) { case t_array: pvalues = op->value.refs; break; case t_mixedarray: case t_shortarray: { int i; for (i = 0; i < 6; ++i) { code = array_get(mem, op, (long)i, &values[i]); if (code < 0) return code; } pvalues = values; } break; default: return_op_typecheck(op); } check_read(*op); if (r_size(op) != 6) return_error(e_rangecheck); code = float_params(pvalues + 5, 6, (float *)pmat); return (code < 0 ? code : 0); }
static 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; if (!r_is_array(op1)) return_op_typecheck(op1); /* Check the length before the write access per CET 28-03 */ size = r_size(op1); depth = count_exec_stack(i_ctx_p, include_marks); if (depth > size) return_error(e_rangecheck); check_write(*op1); { 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; }
static int zrunandhide(i_ctx_t *i_ctx_p) { os_ptr op = osp; es_ptr ep; 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; }
/* <array|dict|name|packedarray|string> length <int> */ static int zlength(i_ctx_t *i_ctx_p) { os_ptr op = osp; switch (r_type(op)) { case t_array: case t_string: case t_mixedarray: case t_shortarray: check_read(*op); make_int(op, r_size(op)); return 0; case t_dictionary: check_dict_read(*op); make_int(op, dict_length(op)); return 0; case t_name: { ref str; name_string_ref(imemory, op, &str); make_int(op, r_size(&str)); return 0; } case t_astruct: if (gs_object_type(imemory, op->value.pstruct) != &st_bytes) return_error(e_typecheck); check_read(*op); make_int(op, gs_object_size(imemory, op->value.pstruct)); return 0; default: return_op_typecheck(op); } }
int zfor(i_ctx_t *i_ctx_p) { os_ptr op = osp; register es_ptr ep; int code; float params[3]; /* Mostly undocumented, and somewhat bizarre Adobe behavior discovered */ /* with the CET (28-05) and FTS (124-01) is that the proc is not run */ /* if BOTH the initial value and increment are zero. */ if ((code = float_params(op - 1, 3, params)) < 0) return code; if ( params[0] == 0.0 && params[1] == 0.0 ) { pop(4); /* don't run the proc */ return 0; } check_estack(7); ep = esp + 6; check_proc(*op); /* Push a mark, the control variable set to 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 { 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; }
/* <string> <index> <int> put - */ static int zput(i_ctx_t *i_ctx_p) { os_ptr op = osp; os_ptr op1 = op - 1; os_ptr op2 = op1 - 1; byte *sdata; uint ssize; switch (r_type(op2)) { case t_dictionary: if (i_ctx_p->in_superexec == 0) check_dict_write(*op2); { int code = idict_put(op2, op1, op); if (code < 0) return code; /* error */ } break; case t_array: check_write(*op2); check_int_ltu(*op1, r_size(op2)); store_check_dest(op2, op); { ref *eltp = op2->value.refs + (uint) op1->value.intval; ref_assign_old(op2, eltp, op, "put"); } break; case t_mixedarray: /* packed arrays are read-only */ case t_shortarray: return_error(e_invalidaccess); case t_string: sdata = op2->value.bytes; ssize = r_size(op2); str: check_write(*op2); check_int_ltu(*op1, ssize); check_int_leu(*op, 0xff); sdata[(uint)op1->value.intval] = (byte)op->value.intval; break; case t_astruct: if (gs_object_type(imemory, op2->value.pstruct) != &st_bytes) return_error(e_typecheck); sdata = r_ptr(op2, byte); ssize = gs_object_size(imemory, op2->value.pstruct); goto str; default: return_op_typecheck(op2); } pop(3); return 0; }
/* <num1> <num2> mul <product> */ int zmul(i_ctx_t *i_ctx_p) { os_ptr op = osp; switch (r_type(op)) { default: return_op_typecheck(op); case t_real: switch (r_type(op - 1)) { default: return_op_typecheck(op - 1); case t_real: op[-1].value.realval *= op->value.realval; break; case t_integer: make_real(op - 1, (double)op[-1].value.intval * op->value.realval); } break; case t_integer: switch (r_type(op - 1)) { default: return_op_typecheck(op - 1); case t_real: op[-1].value.realval *= (double)op->value.intval; break; case t_integer: { double ab = (double)op[-1].value.intval * op->value.intval; if (ab > 2147483647.) /* (double)0x7fffffff */ make_real(op - 1, ab); else if (ab < -2147483648.) /* (double)(int)0x80000000 */ make_real(op - 1, ab); else op[-1].value.intval = (int)ab; } } } pop(1); return 0; }
/* <num1> <num2> div <real_quotient> */ int zdiv(i_ctx_t *i_ctx_p) { os_ptr op = osp; os_ptr op1 = op - 1; /* We can't use the non_int_cases macro, */ /* because we have to check explicitly for op == 0. */ switch (r_type(op)) { default: return_op_typecheck(op); case t_real: if (op->value.realval == 0) return_error(e_undefinedresult); switch (r_type(op1)) { default: return_op_typecheck(op1); case t_real: op1->value.realval /= op->value.realval; break; case t_integer: make_real(op1, (double)op1->value.intval / op->value.realval); } break; case t_integer: if (op->value.intval == 0) return_error(e_undefinedresult); switch (r_type(op1)) { default: return_op_typecheck(op1); case t_real: op1->value.realval /= (double)op->value.intval; break; case t_integer: make_real(op1, (double)op1->value.intval / (double)op->value.intval); } } pop(1); return 0; }
/* <array> <offset> setdash - */ static int zsetdash(i_ctx_t *i_ctx_p) { os_ptr op = osp; os_ptr op1 = op - 1; double offset; int code = real_param(op, &offset); uint i, n; gs_memory_t *mem = imemory; float *pattern; if (code < 0) return_op_typecheck(op); if (!r_is_array(op1)) return_op_typecheck(op1); /* Adobe interpreters apparently don't check the array for */ /* read access, so we won't either. */ /*check_read(*op1); */ /* Unpack the dash pattern and check it */ n = r_size(op1); pattern = (float *)gs_alloc_byte_array(mem, n, sizeof(float), "setdash"); if (pattern == 0) return_error(e_VMerror); for (i = 0, code = 0; i < n && code >= 0; ++i) { ref element; array_get(mem, op1, (long)i, &element); code = float_param(&element, &pattern[i]); } if (code >= 0) code = gs_setdash(igs, pattern, n, offset); gs_free_object(mem, pattern, "setdash"); /* gs_setdash copies this */ if (code < 0) return code; ref_assign(&istate->dash_pattern_array, op1); pop(2); return code; }
/* Calculate bonding box of a box transformed by a matrix. */ static int zbbox_transform(i_ctx_t *i_ctx_p) { os_ptr op = osp; gs_matrix m; float bbox[4]; gs_point aa, az, za, zz; double temp; int code; if ((code = read_matrix(imemory, op, &m)) < 0) return code; if (!r_is_array(op - 1)) return_op_typecheck(op - 1); check_read(op[-1]); if (r_size(op - 1) != 4) return_error(gs_error_rangecheck); if ((code = process_float_array(imemory, op - 1, 4, bbox) < 0)) return code; gs_point_transform(bbox[0], bbox[1], &m, &aa); gs_point_transform(bbox[0], bbox[3], &m, &az); gs_point_transform(bbox[2], bbox[1], &m, &za); gs_point_transform(bbox[2], bbox[3], &m, &zz); if ( aa.x > az.x) temp = aa.x, aa.x = az.x, az.x = temp; if ( za.x > zz.x) temp = za.x, za.x = zz.x, zz.x = temp; if ( za.x < aa.x) aa.x = za.x; /* min */ if ( az.x > zz.x) zz.x = az.x; /* max */ if ( aa.y > az.y) temp = aa.y, aa.y = az.y, az.y = temp; if ( za.y > zz.y) temp = za.y, za.y = zz.y, zz.y = temp; if ( za.y < aa.y) aa.y = za.y; /* min */ if ( az.y > zz.y) zz.y = az.y; /* max */ push(2); make_real(op - 3, (float)aa.x); make_real(op - 2, (float)aa.y); make_real(op - 1, (float)zz.x); make_real(op , (float)zz.y); return 0; }
/* <alpha> setalpha - */ static int zsetalpha(i_ctx_t *i_ctx_p) { os_ptr op = osp; double alpha; int code; if (real_param(op, &alpha) < 0) return_op_typecheck(op); if ((code = gs_setalpha(igs, alpha)) < 0) return code; pop(1); return 0; }
/* <smoothness> setsmoothness - */ static int zsetsmoothness(i_ctx_t *i_ctx_p) { os_ptr op = osp; double smoothness; int code; if (real_param(op, &smoothness) < 0) return_op_typecheck(op); if ((code = gs_setsmoothness(igs, smoothness)) < 0) return code; pop(1); return 0; }
/* <num1> round <num2> */ int zround(i_ctx_t *i_ctx_p) { os_ptr op = osp; switch (r_type(op)) { default: return_op_typecheck(op); case t_real: op->value.realval = floor(op->value.realval + 0.5); case t_integer:; } return 0; }
static int zset_real(i_ctx_t *i_ctx_p, int (*set_proc)(gs_state *, floatp)) { os_ptr op = osp; double param; int code = real_param(op, ¶m); if (code < 0) return_op_typecheck(op); code = set_proc(igs, param); if (!code) pop(1); return code; }
/* <num1> ceiling <num2> */ int zceiling(i_ctx_t *i_ctx_p) { os_ptr op = osp; switch (r_type(op)) { default: return_op_typecheck(op); case t_real: op->value.realval = ceil(op->value.realval); case t_integer:; } return 0; }
/* the interpreter will almost always call it directly. */ int zop_sub(register os_ptr op) { switch (r_type(op)) { default: return_op_typecheck(op); case t_real: switch (r_type(op - 1)) { default: return_op_typecheck(op - 1); case t_real: op[-1].value.realval -= op->value.realval; break; case t_integer: make_real(op - 1, (double)op[-1].value.intval - op->value.realval); } break; case t_integer: switch (r_type(op - 1)) { default: return_op_typecheck(op - 1); case t_real: op[-1].value.realval -= (double)op->value.intval; break; case t_integer: { int int1 = op[-1].value.intval; if ((int1 ^ (op[-1].value.intval = int1 - op->value.intval)) < 0 && (int1 ^ op->value.intval) < 0 ) { /* Overflow, convert to real */ make_real(op - 1, (float)int1 - op->value.intval); } } } } return 0; }
/* the interpreter will almost always call it directly. */ int zop_add(register os_ptr op) { switch (r_type(op)) { default: return_op_typecheck(op); case t_real: switch (r_type(op - 1)) { default: return_op_typecheck(op - 1); case t_real: op[-1].value.realval += op->value.realval; break; case t_integer: make_real(op - 1, (double)op[-1].value.intval + op->value.realval); } break; case t_integer: switch (r_type(op - 1)) { default: return_op_typecheck(op - 1); case t_real: op[-1].value.realval += (double)op->value.intval; break; case t_integer: { int int2 = op->value.intval; if (((op[-1].value.intval += int2) ^ int2) < 0 && ((op[-1].value.intval - int2) ^ int2) >= 0 ) { /* Overflow, convert to real */ make_real(op - 1, (double)(op[-1].value.intval - int2) + int2); } } } } return 0; }
int ztoken(i_ctx_t *i_ctx_p) { os_ptr op = osp; switch (r_type(op)) { default: return_op_typecheck(op); case t_file: { stream *s; scanner_state state; check_read_file(i_ctx_p, s, op); check_ostack(1); gs_scanner_init(&state, op); return token_continue(i_ctx_p, &state, true); } case t_string: { ref token; /* -1 is to remove the string operand in case of error. */ int orig_ostack_depth = ref_stack_count(&o_stack) - 1; int code; /* Don't pop the operand in case of invalidaccess. */ if (!r_has_attr(op, a_read)) return_error(e_invalidaccess); code = gs_scan_string_token(i_ctx_p, op, &token); switch (code) { case scan_EOF: /* no tokens */ make_false(op); return 0; default: if (code < 0) { /* * Clear anything that may have been left on the ostack, * including the string operand. */ if (orig_ostack_depth < ref_stack_count(&o_stack)) pop(ref_stack_count(&o_stack)- orig_ostack_depth); return code; } } push(2); op[-1] = token; make_true(op); return 0; } } }
/* its length; nothing else has been checked. */ static int copy_interval(i_ctx_t *i_ctx_p /* for ref_assign_old */, os_ptr prto, uint index, os_ptr prfrom, client_name_t cname) { int fromtype = r_type(prfrom); uint fromsize = r_size(prfrom); if (!(fromtype == r_type(prto) || ((fromtype == t_shortarray || fromtype == t_mixedarray) && r_type(prto) == t_array)) ) return_op_typecheck(prfrom); check_read(*prfrom); check_write(*prto); if (fromsize > r_size(prto) - index) return_error(e_rangecheck); switch (fromtype) { case t_array: { /* We have to worry about aliasing, */ /* but refcpy_to_old takes care of it for us. */ return refcpy_to_old(prto, index, prfrom->value.refs, fromsize, idmemory, cname); } case t_string: { /* memmove takes care of aliasing. */ memmove(prto->value.bytes + index, prfrom->value.bytes, fromsize); } break; case t_mixedarray: case t_shortarray: { /* We don't have to worry about aliasing, because */ /* packed arrays are read-only and hence the destination */ /* can't be a packed array. */ uint i; const ref_packed *packed = prfrom->value.packed; ref *pdest = prto->value.refs + index; ref elt; for (i = 0; i < fromsize; i++, pdest++) { packed_get(imemory, packed, &elt); ref_assign_old(prto, pdest, &elt, cname); packed = packed_next(packed); } } break; } return 0; }
static int zforall(i_ctx_t *i_ctx_p) { os_ptr op = osp; os_ptr obj = op - 1; es_ptr ep = esp; es_ptr cproc = ep + 4; check_estack(6); check_proc(*op); switch (r_type(obj)) { default: return_op_typecheck(obj); case t_array: check_read(*obj); make_op_estack(cproc, array_continue); break; case t_dictionary: check_dict_read(*obj); make_int(cproc, dict_first(obj)); ++cproc; make_op_estack(cproc, dict_continue); break; case t_string: check_read(*obj); make_op_estack(cproc, string_continue); break; case t_mixedarray: case t_shortarray: check_read(*obj); make_op_estack(cproc, packedarray_continue); break; } /* * Push: * - a mark; * - the composite object; * - the procedure; * - the iteration index (only for dictionaries, done above); * and invoke the continuation operator. */ make_mark_estack(ep + 1, es_for, forall_cleanup); ep[2] = *obj; ep[3] = *op; esp = cproc - 1; pop(2); return (*real_opproc(cproc))(i_ctx_p); }
/* <array> dictstack <subarray> */ static int zdictstack(i_ctx_t *i_ctx_p) { os_ptr op = osp; uint count = ref_stack_count(&d_stack); if (!level2_enabled) count--; /* see dstack.h */ if (!r_is_array(op)) return_op_typecheck(op); if (r_size(op) < count) return_error(e_rangecheck); if (!r_has_type_attrs(op, t_array, a_write)) return_error(e_invalidaccess); return ref_stack_store(&d_stack, op, count, 0, 0, true, idmemory, "dictstack"); }
/* <int> not <int> */ int znot(i_ctx_t *i_ctx_p) { os_ptr op = osp; switch (r_type(op)) { case t_boolean: op->value.boolval = !op->value.boolval; break; case t_integer: op->value.intval = ~op->value.intval; break; default: return_op_typecheck(op); } return 0; }
/* <num1> truncate <num2> */ int ztruncate(i_ctx_t *i_ctx_p) { os_ptr op = osp; switch (r_type(op)) { default: return_op_typecheck(op); case t_real: op->value.realval = (op->value.realval < 0.0 ? ceil(op->value.realval) : floor(op->value.realval)); case t_integer:; } return 0; }
/* <seq:array|packedarray|string> <index> <count> getinterval <subseq> */ static int zgetinterval(i_ctx_t *i_ctx_p) { os_ptr op = osp; os_ptr op1 = op - 1; os_ptr op2 = op1 - 1; uint index; uint count; switch (r_type(op2)) { default: return_op_typecheck(op2); case t_array: case t_string: case t_mixedarray: case t_shortarray:; } check_read(*op2); check_int_leu(*op1, r_size(op2)); index = op1->value.intval; check_int_leu(*op, r_size(op2) - index); count = op->value.intval; switch (r_type(op2)) { case t_array: op2->value.refs += index; break; case t_string: op2->value.bytes += index; break; case t_mixedarray: { const ref_packed *packed = op2->value.packed; for (; index--;) packed = packed_next(packed); op2->value.packed = packed; break; } case t_shortarray: op2->value.packed += index; break; } r_set_size(op2, count); pop(2); return 0; }
/* <num> setlinewidth - */ static int zsetlinewidth(i_ctx_t *i_ctx_p) { os_ptr op = osp; /* * The Red Book doesn't say anything about this, but Adobe * interpreters return (or perhaps store) the absolute value * of the width. */ double width; int code = real_param(op, &width); if (code < 0) return_op_typecheck(op); code = gs_setlinewidth(igs, fabs(width)); if (code >= 0) pop(1); return code; }
/* but not for dictionaries (see zcopy_dict in zdict.c). */ int zcopy(i_ctx_t *i_ctx_p) { os_ptr op = osp; int type = r_type(op); if (type == t_integer) return zcopy_integer(i_ctx_p); check_op(2); switch (type) { case t_array: case t_string: return zcopy_interval(i_ctx_p); case t_dictionary: return zcopy_dict(i_ctx_p); default: return_op_typecheck(op); } }
/* <num1> neg <num2> */ int zneg(i_ctx_t *i_ctx_p) { os_ptr op = osp; switch (r_type(op)) { default: return_op_typecheck(op); case t_real: op->value.realval = -op->value.realval; break; case t_integer: if (op->value.intval == MIN_INTVAL) make_real(op, -(float)MIN_INTVAL); else op->value.intval = -op->value.intval; } return 0; }
/* <num1> abs <num2> */ int zabs(i_ctx_t *i_ctx_p) { os_ptr op = osp; switch (r_type(op)) { default: return_op_typecheck(op); case t_real: if (op->value.realval >= 0) return 0; break; case t_integer: if (op->value.intval >= 0) return 0; break; } return zneg(i_ctx_p); }