1 /* Copyright (C) 2001-2009 Artifex Software, Inc.
2 All Rights Reserved.
3
4 This software is provided AS-IS with no warranty, either express or
5 implied.
6
7 This software is distributed under license and may not be copied, modified
8 or distributed except as expressly authorized under the terms of that
9 license. Refer to licensing information at http://www.artifex.com/
10 or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
11 San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
12 */
13
14 /* $Id: zmisc.c 9334 2009-01-08 09:17:18Z ghostgum $ */
15 /* Miscellaneous operators */
16
17 #include "errno_.h"
18 #include "memory_.h"
19 #include "string_.h"
20 #include "ghost.h"
21 #include "gscdefs.h" /* for gs_serialnumber */
22 #include "gp.h"
23 #include "oper.h"
24 #include "ialloc.h"
25 #include "idict.h"
26 #include "dstack.h" /* for name lookup in bind */
27 #include "iname.h"
28 #include "ipacked.h"
29 #include "ivmspace.h"
30 #include "store.h"
31
32 /**********************************************************************/
33
34 /* <proc> bind <proc> */
35 static inline bool
r_is_ex_oper(const ref * rp)36 r_is_ex_oper(const ref *rp)
37 {
38 return (r_has_attr(rp, a_executable) &&
39 (r_btype(rp) == t_operator || r_type(rp) == t_oparray));
40 }
41 static int
zbind(i_ctx_t * i_ctx_p)42 zbind(i_ctx_t *i_ctx_p)
43 {
44 os_ptr op = osp;
45 uint depth = 1;
46 ref defn;
47 register os_ptr bsp;
48
49 switch (r_type(op)) {
50 case t_array:
51 if (!r_has_attr(op, a_write)) {
52 return 0; /* per PLRM3 */
53 }
54 case t_mixedarray:
55 case t_shortarray:
56 defn = *op;
57 break;
58 case t_oparray:
59 defn = *op->value.const_refs;
60 break;
61 default:
62 return_op_typecheck(op);
63 }
64 push(1);
65 *op = defn;
66 bsp = op;
67 /*
68 * We must not make the top-level procedure read-only,
69 * but we must bind it even if it is read-only already.
70 *
71 * Here are the invariants for the following loop:
72 * `depth' elements have been pushed on the ostack;
73 * For i < depth, p = ref_stack_index(&o_stack, i):
74 * *p is an array (or packedarray) ref.
75 */
76 while (depth) {
77 while (r_size(bsp)) {
78 ref_packed *const tpp = (ref_packed *)bsp->value.packed; /* break const */
79
80 r_dec_size(bsp, 1);
81 if (r_is_packed(tpp)) {
82 /* Check for a packed executable name */
83 ushort elt = *tpp;
84
85 if (r_packed_is_exec_name(&elt)) {
86 ref nref;
87 ref *pvalue;
88
89 name_index_ref(imemory, packed_name_index(&elt),
90 &nref);
91 if ((pvalue = dict_find_name(&nref)) != 0 &&
92 r_is_ex_oper(pvalue)
93 ) {
94 store_check_dest(bsp, pvalue);
95 /*
96 * Always save the change, since this can only
97 * happen once.
98 */
99 ref_do_save(bsp, tpp, "bind");
100 *tpp = pt_tag(pt_executable_operator) +
101 op_index(pvalue);
102 }
103 }
104 bsp->value.packed = tpp + 1;
105 } else {
106 ref *const tp = bsp->value.refs++;
107
108 switch (r_type(tp)) {
109 case t_name: /* bind the name if an operator */
110 if (r_has_attr(tp, a_executable)) {
111 ref *pvalue;
112
113 if ((pvalue = dict_find_name(tp)) != 0 &&
114 r_is_ex_oper(pvalue)
115 ) {
116 store_check_dest(bsp, pvalue);
117 ref_assign_old(bsp, tp, pvalue, "bind");
118 }
119 }
120 break;
121 case t_array: /* push into array if writable */
122 if (!r_has_attr(tp, a_write))
123 break;
124 case t_mixedarray:
125 case t_shortarray:
126 if (r_has_attr(tp, a_executable)) {
127 /* Make reference read-only */
128 r_clear_attrs(tp, a_write);
129 if (bsp >= ostop) {
130 /* Push a new stack block. */
131 ref temp;
132 int code;
133
134 temp = *tp;
135 osp = bsp;
136 code = ref_stack_push(&o_stack, 1);
137 if (code < 0) {
138 ref_stack_pop(&o_stack, depth);
139 return_error(code);
140 }
141 bsp = osp;
142 *bsp = temp;
143 } else
144 *++bsp = *tp;
145 depth++;
146 }
147 }
148 }
149 }
150 bsp--;
151 depth--;
152 if (bsp < osbot) { /* Pop back to the previous stack block. */
153 osp = bsp;
154 ref_stack_pop_block(&o_stack);
155 bsp = osp;
156 }
157 }
158 osp = bsp;
159 return 0;
160 }
161
162 /* - serialnumber <int> */
163 static int
zserialnumber(i_ctx_t * i_ctx_p)164 zserialnumber(i_ctx_t *i_ctx_p)
165 {
166 os_ptr op = osp;
167
168 push(1);
169 make_int(op, gs_serialnumber);
170 return 0;
171 }
172
173 /* some FTS tests work better if realtime starts from 0 at boot time */
174 static long real_time_0[2];
175
176 static int
zmisc_init_realtime(i_ctx_t * i_ctx_p)177 zmisc_init_realtime(i_ctx_t * i_ctx_p)
178 {
179 gp_get_realtime(real_time_0);
180 return 0;
181 }
182
183 /* - realtime <int> */
184 static int
zrealtime(i_ctx_t * i_ctx_p)185 zrealtime(i_ctx_t *i_ctx_p)
186 {
187 os_ptr op = osp;
188 long secs_ns[2];
189
190 gp_get_realtime(secs_ns);
191 secs_ns[1] -= real_time_0[1];
192 secs_ns[0] -= real_time_0[0];
193 push(1);
194 make_int(op, secs_ns[0] * 1000 + secs_ns[1] / 1000000);
195 return 0;
196 }
197
198 /* - usertime <int> */
199 static int
zusertime(i_ctx_t * i_ctx_p)200 zusertime(i_ctx_t *i_ctx_p)
201 {
202 os_ptr op = osp;
203 long secs_ns[2];
204
205 gp_get_usertime(secs_ns);
206 push(1);
207 make_int(op, secs_ns[0] * 1000 + secs_ns[1] / 1000000);
208 return 0;
209 }
210
211 /* ---------------- Non-standard operators ---------------- */
212
213 /* <string> getenv <value_string> true */
214 /* <string> getenv false */
215 static int
zgetenv(i_ctx_t * i_ctx_p)216 zgetenv(i_ctx_t *i_ctx_p)
217 {
218 os_ptr op = osp;
219 char *str;
220 byte *value;
221 int len = 0;
222
223 check_read_type(*op, t_string);
224 str = ref_to_string(op, imemory, "getenv key");
225 if (str == 0)
226 return_error(e_VMerror);
227 if (gp_getenv(str, (char *)0, &len) > 0) { /* key missing */
228 ifree_string((byte *) str, r_size(op) + 1, "getenv key");
229 make_false(op);
230 return 0;
231 }
232 value = ialloc_string(len, "getenv value");
233 if (value == 0) {
234 ifree_string((byte *) str, r_size(op) + 1, "getenv key");
235 return_error(e_VMerror);
236 }
237 DISCARD(gp_getenv(str, (char *)value, &len)); /* can't fail */
238 ifree_string((byte *) str, r_size(op) + 1, "getenv key");
239 /* Delete the stupid C string terminator. */
240 value = iresize_string(value, len, len - 1,
241 "getenv value"); /* can't fail */
242 push(1);
243 make_string(op - 1, a_all | icurrent_space, len - 1, value);
244 make_true(op);
245 return 0;
246 }
247
248 /* - .defaultpapersize <string> true */
249 /* - .defaultpapersize false */
250 static int
zdefaultpapersize(i_ctx_t * i_ctx_p)251 zdefaultpapersize(i_ctx_t *i_ctx_p)
252 {
253 os_ptr op = osp;
254 byte *value;
255 int len = 0;
256
257 if (gp_defaultpapersize((char *)0, &len) > 0) {
258 /* no default paper size */
259 push(1);
260 make_false(op);
261 return 0;
262 }
263
264 value = ialloc_string(len, "defaultpapersize value");
265 if (value == 0) {
266 return_error(e_VMerror);
267 }
268 DISCARD(gp_defaultpapersize((char *)value, &len)); /* can't fail */
269 /* Delete the stupid C string terminator. */
270 value = iresize_string(value, len, len - 1,
271 "defaultpapersize value"); /* can't fail */
272 push(2);
273 make_string(op - 1, a_all | icurrent_space, len - 1, value);
274 make_true(op);
275 return 0;
276 }
277
278 /* <name> <proc> .makeoperator <oper> */
279 static int
zmakeoperator(i_ctx_t * i_ctx_p)280 zmakeoperator(i_ctx_t *i_ctx_p)
281 {
282 os_ptr op = osp;
283 op_array_table *opt;
284 uint count;
285 ref *tab;
286
287 check_type(op[-1], t_name);
288 check_proc(*op);
289 switch (r_space(op)) {
290 case avm_global:
291 opt = &op_array_table_global;
292 break;
293 case avm_local:
294 opt = &op_array_table_local;
295 break;
296 default:
297 return_error(e_invalidaccess);
298 }
299 count = opt->count;
300 tab = opt->table.value.refs;
301 /*
302 * restore doesn't reset op_array_table.count, but it does
303 * remove entries from op_array_table.table. Since we fill
304 * the table in order, we can detect that a restore has occurred
305 * by checking whether what should be the most recent entry
306 * is occupied. If not, we scan backwards over the vacated entries
307 * to find the true end of the table.
308 */
309 while (count > 0 && r_has_type(&tab[count - 1], t_null))
310 --count;
311 if (count == r_size(&opt->table))
312 return_error(e_limitcheck);
313 ref_assign_old(&opt->table, &tab[count], op, "makeoperator");
314 opt->nx_table[count] = name_index(imemory, op - 1);
315 op_index_ref(opt->base_index + count, op - 1);
316 opt->count = count + 1;
317 pop(1);
318 return 0;
319 }
320
321 /* - .oserrno <int> */
322 static int
zoserrno(i_ctx_t * i_ctx_p)323 zoserrno(i_ctx_t *i_ctx_p)
324 {
325 os_ptr op = osp;
326
327 push(1);
328 make_int(op, errno);
329 return 0;
330 }
331
332 /* <int> .setoserrno - */
333 static int
zsetoserrno(i_ctx_t * i_ctx_p)334 zsetoserrno(i_ctx_t *i_ctx_p)
335 {
336 os_ptr op = osp;
337
338 check_type(*op, t_integer);
339 errno = op->value.intval;
340 pop(1);
341 return 0;
342 }
343
344 /* <int> .oserrorstring <string> true */
345 /* <int> .oserrorstring false */
346 static int
zoserrorstring(i_ctx_t * i_ctx_p)347 zoserrorstring(i_ctx_t *i_ctx_p)
348 {
349 os_ptr op = osp;
350 const char *str;
351 int code;
352 uint len;
353 byte ch;
354
355 check_type(*op, t_integer);
356 str = gp_strerror((int)op->value.intval);
357 if (str == 0 || (len = strlen(str)) == 0) {
358 make_false(op);
359 return 0;
360 }
361 check_ostack(1);
362 code = string_to_ref(str, op, iimemory, ".oserrorstring");
363 if (code < 0)
364 return code;
365 /* Strip trailing end-of-line characters. */
366 while ((len = r_size(op)) != 0 &&
367 ((ch = op->value.bytes[--len]) == '\r' || ch == '\n')
368 )
369 r_dec_size(op, 1);
370 push(1);
371 make_true(op);
372 return 0;
373 }
374
375 /* <string> <bool> .setdebug - */
376 static int
zsetdebug(i_ctx_t * i_ctx_p)377 zsetdebug(i_ctx_t *i_ctx_p)
378 {
379 os_ptr op = osp;
380 check_read_type(op[-1], t_string);
381 check_type(*op, t_boolean);
382 {
383 int i;
384
385 for (i = 0; i < r_size(op - 1); i++)
386 gs_debug[op[-1].value.bytes[i] & 127] =
387 op->value.boolval;
388 }
389 pop(2);
390 return 0;
391 }
392
393 /* There are a few cases where a customer/user might want CPSI behavior
394 * instead of the GS default behavior. cmyk_to_rgb and Type 1 char fill
395 * method are two that have come up so far. This operator allows a PS
396 * program to control the behavior without needing to recompile
397 *
398 * While this would better if it were in some context 'state', we use
399 * a global, which we don't really like, but at least it is better
400 * than a compile time #define, as in #USE_ADOBE_CMYK_RGB and allows
401 * us to easily test with/without and not require recompilation.
402 */
403 extern bool CPSI_mode; /* not worth polluting a header file */
404
405 /* <bool> .setCPSImode - */
406 static int
zsetCPSImode(i_ctx_t * i_ctx_p)407 zsetCPSImode(i_ctx_t *i_ctx_p)
408 {
409 os_ptr op = osp;
410 check_type(*op, t_boolean);
411 CPSI_mode = op->value.boolval;
412 pop(1);
413 return 0;
414 }
415
416 /* - .getCPSImode <bool> */
417 static int
zgetCPSImode(i_ctx_t * i_ctx_p)418 zgetCPSImode(i_ctx_t *i_ctx_p)
419 {
420 os_ptr op = osp;
421
422 push(1);
423 make_bool(op, CPSI_mode);
424 return 0;
425 }
426
427 /* ------ gs persistent cache operators ------ */
428 /* these are for testing only. they're disabled in the normal build
429 * to prevent access to the cache by malicious postscript files
430 *
431 * use something like this:
432 * (value) (key) .pcacheinsert
433 * (key) .pcachequery { (\n) concatstrings print } if
434 */
435
436 #ifdef DEBUG_CACHE
437
438 /* <string> <string> .pcacheinsert */
439 static int
zpcacheinsert(i_ctx_t * i_ctx_p)440 zpcacheinsert(i_ctx_t *i_ctx_p)
441 {
442 os_ptr op = osp;
443 char *key, *buffer;
444 int keylen, buflen;
445 int code = 0;
446
447 check_read_type(*op, t_string);
448 keylen = r_size(op);
449 key = op->value.bytes;
450 check_read_type(*(op - 1), t_string);
451 buflen = r_size(op - 1);
452 buffer = (op - 1)->value.bytes;
453
454 code = gp_cache_insert(0, key, keylen, buffer, buflen);
455 if (code < 0)
456 return code;
457
458 pop(2);
459
460 return code;
461 }
462
463 /* allocation callback for query result */
464 static void *
pcache_alloc_callback(void * userdata,int bytes)465 pcache_alloc_callback(void *userdata, int bytes)
466 {
467 i_ctx_t *i_ctx_p = (i_ctx_t*)userdata;
468 return ialloc_string(bytes, "pcache buffer");
469 }
470
471 /* <string> .pcachequery <string> true */
472 /* <string> .pcachequery false */
473 static int
zpcachequery(i_ctx_t * i_ctx_p)474 zpcachequery(i_ctx_t *i_ctx_p)
475 {
476 os_ptr op = osp;
477 int len;
478 char *key;
479 byte *string;
480 int code = 0;
481
482 check_read_type(*op, t_string);
483 len = r_size(op);
484 key = op->value.bytes;
485 len = gp_cache_query(GP_CACHE_TYPE_TEST, key, len, (void**)&string, &pcache_alloc_callback, i_ctx_p);
486 if (len < 0) {
487 make_false(op);
488 return 0;
489 }
490 if (string == NULL)
491 return_error(e_VMerror);
492 make_string(op, a_all | icurrent_space, len, string);
493
494 push(1);
495 make_true(op);
496
497 return code;
498 }
499
500 #endif /* DEBUG_CACHE */
501
502 /* ------ Initialization procedure ------ */
503
504 const op_def zmisc_op_defs[] =
505 {
506 {"1bind", zbind},
507 {"1getenv", zgetenv},
508 {"0.defaultpapersize", zdefaultpapersize},
509 {"2.makeoperator", zmakeoperator},
510 {"0.oserrno", zoserrno},
511 {"1.oserrorstring", zoserrorstring},
512 {"0realtime", zrealtime},
513 {"1serialnumber", zserialnumber},
514 {"2.setdebug", zsetdebug},
515 {"1.setoserrno", zsetoserrno},
516 {"0usertime", zusertime},
517 {"1.setCPSImode", zsetCPSImode},
518 {"0.getCPSImode", zgetCPSImode},
519 #ifdef DEBUG_CACHE
520 /* pcache test */
521 {"2.pcacheinsert", zpcacheinsert},
522 {"1.pcachequery", zpcachequery},
523 #endif
524 op_def_end(zmisc_init_realtime)
525 };
526