static int zwritecvp_at(i_ctx_t *i_ctx_p, os_ptr op, uint start, bool first) { stream *s; byte str[100]; /* arbitrary */ ref rstr; const byte *data = str; uint len; int code, status; check_write_file(s, op - 2); check_type(*op, t_integer); code = obj_cvp(op - 1, str, sizeof(str), &len, (int)op->value.intval, start, imemory, true); if (code == e_rangecheck) { code = obj_string_data(imemory, op - 1, &data, &len); if (len < start) return_error(e_rangecheck); data += start; len -= start; } if (code < 0) return code; r_set_size(&rstr, len); rstr.value.const_bytes = data; status = write_string(&rstr, s); switch (status) { default: return_error(e_ioerror); case 0: break; case INTC: case CALLC: len = start + len - r_size(&rstr); if (!first) --osp; /* pop(1) without affecting op */ return handle_write_status(i_ctx_p, status, op - 2, &len, zwritecvp_continue); } if (code == 1) { if (first) check_ostack(1); push_op_estack(zwritecvp_continue); if (first) push(1); make_int(osp, start + len); return o_push_estack; } if (first) /* zwritecvp */ pop(3); else /* zwritecvp_continue */ pop(4); return 0; }
/* * Create a printable representation of an object, a la cvs and =. Return 0 * if OK, e_rangecheck if the destination wasn't large enough, * e_invalidaccess if the object's contents weren't readable. If pchars != * NULL, then if the object was a string or name, store a pointer to its * characters in *pchars even if it was too large; otherwise, set *pchars = * str. In any case, store the length in *prlen. */ int obj_cvs(const gs_memory_t *mem, const ref * op, byte * str, uint len, uint * prlen, const byte ** pchars) { int code = obj_cvp(op, str, len, prlen, 0, 0, mem, false); /* NB: NULL memptr */ if (code != 1 && pchars) { *pchars = str; return code; } obj_string_data(mem, op, pchars, prlen); return gs_note_error(e_rangecheck); }
int obj_cvp(const ref * op, byte * str, uint len, uint * prlen, int full_print, uint start_pos, const gs_memory_t *mem, bool restart) { char buf[50]; /* big enough for any float, double, or struct name */ const byte *data = (const byte *)buf; uint size; int code; ref nref; if (full_print) { static const char * const type_strings[] = { REF_TYPE_PRINT_STRINGS }; switch (r_btype(op)) { case t_boolean: case t_integer: break; case t_real: { /* * To get fully accurate output results for IEEE * single-precision floats (24 bits of mantissa), the ANSI %g * default of 6 digits is not enough; 9 are needed. * Unfortunately, using %.9g for floats (as opposed to doubles) * produces unfortunate artifacts such as 0.01 5 mul printing as * 0.049999997. Therefore, we print using %g, and if the result * isn't accurate enough, print again using %.9g. * Unfortunately, a few PostScript programs 'know' that the * printed representation of floats fits into 6 digits (e.g., * with cvs). We resolve this by letting cvs, cvrs, and = do * what the Adobe interpreters appear to do (use %g), and only * produce accurate output for ==, for which there is no * analogue of cvs. What a hack! */ float value = op->value.realval; float scanned; sprintf(buf, "%g", value); sscanf(buf, "%f", &scanned); if (scanned != value) sprintf(buf, "%.9g", value); ensure_dot(buf); goto rs; } case t_operator: case t_oparray: code = obj_cvp(op, (byte *)buf + 2, sizeof(buf) - 4, &size, 0, 0, mem, restart); if (code < 0) return code; buf[0] = buf[1] = buf[size + 2] = buf[size + 3] = '-'; size += 4; goto nl; case t_name: if (r_has_attr(op, a_executable)) { code = obj_string_data(mem, op, &data, &size); if (code < 0) return code; goto nl; } if (start_pos > 0) return obj_cvp(op, str, len, prlen, 0, start_pos - 1, mem, restart); if (len < 1) return_error(e_rangecheck); code = obj_cvp(op, str + 1, len - 1, prlen, 0, 0, mem, restart); if (code < 0) return code; str[0] = '/'; ++*prlen; return code; case t_null: data = (const byte *)"null"; goto rs; case t_string: if (!r_has_attr(op, a_read)) goto other; size = r_size(op); { bool truncate = (full_print == 1 && size > CVP_MAX_STRING); stream_cursor_read r; stream_cursor_write w; uint skip; byte *wstr; uint len1; int status = 1; if (start_pos == 0) { if (len < 1) return_error(e_rangecheck); str[0] = '('; skip = 0; wstr = str + 1; } else { skip = start_pos - 1; wstr = str; } len1 = len + (str - wstr); r.ptr = op->value.const_bytes - 1; r.limit = r.ptr + (truncate ? CVP_MAX_STRING : size); while (skip && status == 1) { uint written; w.ptr = (byte *)buf - 1; w.limit = w.ptr + min(skip + len1, sizeof(buf)); status = s_PSSE_template.process(NULL, &r, &w, false); written = w.ptr - ((byte *)buf - 1); if (written > skip) { written -= skip; memcpy(wstr, buf + skip, written); wstr += written; skip = 0; break; } skip -= written; } /* * We can reach here with status == 0 (and skip != 0) if * start_pos lies within the trailing ")" or "...)". */ if (status == 0) { #ifdef DEBUG if (skip > (truncate ? 4 : 1)) { return_error(e_Fatal); } #endif } w.ptr = wstr - 1; w.limit = str - 1 + len; if (status == 1) status = s_PSSE_template.process(NULL, &r, &w, false); *prlen = w.ptr - (str - 1); if (status != 0) return 1; if (truncate) { if (len - *prlen < 4 - skip) return 1; memcpy(w.ptr + 1, "...)" + skip, 4 - skip); *prlen += 4 - skip; } else { if (len - *prlen < 1 - skip) return 1; memcpy(w.ptr + 1, ")" + skip, 1 - skip); *prlen += 1 - skip; } } return 0; case t_astruct: case t_struct: if (r_is_foreign(op)) { /* gs_object_type may not work. */ data = (const byte *)"-foreign-struct-"; goto rs; } if (!mem) { data = (const byte *)"-(struct)-"; goto rs; } data = (const byte *) gs_struct_type_name_string( gs_object_type(mem, (const obj_header_t *)op->value.pstruct)); size = strlen((const char *)data); if (size > 4 && !memcmp(data + size - 4, "type", 4)) size -= 4; if (size > sizeof(buf) - 2) return_error(e_rangecheck); buf[0] = '-'; memcpy(buf + 1, data, size); buf[size + 1] = '-'; size += 2; data = (const byte *)buf; goto nl; default: other: { int rtype = r_btype(op); if (rtype > countof(type_strings)) return_error(e_rangecheck); data = (const byte *)type_strings[rtype]; if (data == 0) return_error(e_rangecheck); } goto rs; } } /* full_print = 0 */ switch (r_btype(op)) { case t_boolean: data = (const byte *)(op->value.boolval ? "true" : "false"); break; case t_integer: sprintf(buf, "%ld", op->value.intval); break; case t_string: check_read(*op); /* falls through */ case t_name: code = obj_string_data(mem, op, &data, &size); if (code < 0) return code; goto nl; case t_oparray: { uint index = op_index(op); const op_array_table *opt = op_index_op_array_table(index); name_index_ref(mem, opt->nx_table[index - opt->base_index], &nref); name_string_ref(mem, &nref, &nref); code = obj_string_data(mem, &nref, &data, &size); if (code < 0) return code; goto nl; } case t_operator: { /* Recover the name from the initialization table. */ uint index = op_index(op); /* * Check the validity of the index. (An out-of-bounds index * is only possible when examining an invalid object using * the debugger.) */ if (index > 0 && index < op_def_count) { data = (const byte *)(op_index_def(index)->oname + 1); break; } /* Internal operator, no name. */ sprintf(buf, "@0x%lx", (ulong) op->value.opproc); break; } case t_real: /* * The value 0.0001 is a boundary case that the Adobe interpreters * print in f-format but at least some gs versions print in * e-format, presumably because of differences in the underlying C * library implementation. Work around this here. */ if (op->value.realval == (float)0.0001) { strcpy(buf, "0.0001"); } else { sprintf(buf, "%g", op->value.realval); } ensure_dot(buf); break; default: data = (const byte *)"--nostringval--"; } rs: size = strlen((const char *)data); nl: if (size < start_pos) return_error(e_rangecheck); if (!restart && size > len) return_error(e_rangecheck); size -= start_pos; *prlen = min(size, len); memmove(str, data + start_pos, *prlen); return (size > len); }