/src/ghostpdl/psi/zdict.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 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, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Dictionary operators */ |
18 | | #include "ghost.h" |
19 | | #include "oper.h" |
20 | | #include "iddict.h" |
21 | | #include "dstack.h" |
22 | | #include "ilevel.h" /* for [count]dictstack */ |
23 | | #include "iname.h" /* for dict_find_name */ |
24 | | #include "ipacked.h" /* for inline dict lookup */ |
25 | | #include "ivmspace.h" |
26 | | #include "store.h" |
27 | | #include "iscan.h" /* for SCAN_PDF_RULES */ |
28 | | |
29 | | /* <int> dict <dict> */ |
30 | | int |
31 | | zdict(i_ctx_t *i_ctx_p) |
32 | 2.42M | { |
33 | 2.42M | os_ptr op = osp; |
34 | | |
35 | 2.42M | check_op(1); |
36 | 2.42M | check_type(*op, t_integer); |
37 | 2.42M | if (op->value.intval < 0) |
38 | 1 | return_error(gs_error_rangecheck); |
39 | 2.42M | return dict_create((uint) op->value.intval, op); |
40 | 2.42M | } |
41 | | |
42 | | /* <dict> maxlength <int> */ |
43 | | static int |
44 | | zmaxlength(i_ctx_t *i_ctx_p) |
45 | 5.57M | { |
46 | 5.57M | os_ptr op = osp; |
47 | | |
48 | 5.57M | check_op(1); |
49 | 5.57M | check_type(*op, t_dictionary); |
50 | 5.57M | check_dict_read(*op); |
51 | 5.57M | make_int(op, dict_maxlength(op)); |
52 | 5.57M | return 0; |
53 | 5.57M | } |
54 | | |
55 | | /* <dict> begin - */ |
56 | | int |
57 | | zbegin(i_ctx_t *i_ctx_p) |
58 | 17.7M | { |
59 | 17.7M | os_ptr op = osp; |
60 | | |
61 | 17.7M | check_op(1); |
62 | 17.7M | check_type(*op, t_dictionary); |
63 | 17.7M | check_dict_read(*op); |
64 | 17.7M | if ( dsp == dstop ) { |
65 | 0 | int code = ref_stack_extend(&d_stack, 1); |
66 | |
|
67 | 0 | if ( code < 0 ) { |
68 | 0 | if (code == gs_error_dictstackoverflow) { |
69 | | /* Adobe doesn't restore the operand that caused stack */ |
70 | | /* overflow. We do the same to match CET 20-02-02 */ |
71 | 0 | pop(1); |
72 | 0 | } |
73 | 0 | return code; |
74 | 0 | } |
75 | 0 | } |
76 | 17.7M | ++dsp; |
77 | 17.7M | ref_assign(dsp, op); |
78 | 17.7M | dict_set_top(); |
79 | 17.7M | pop(1); |
80 | 17.7M | return 0; |
81 | 17.7M | } |
82 | | |
83 | | /* - end - */ |
84 | | int |
85 | | zend(i_ctx_t *i_ctx_p) |
86 | 17.7M | { |
87 | 17.7M | if (ref_stack_count_inline(&d_stack) == min_dstack_size) { |
88 | | /* We would underflow the d-stack. */ |
89 | 32 | return_error(gs_error_dictstackunderflow); |
90 | 32 | } |
91 | 17.7M | while (dsp == dsbot) { |
92 | | /* We would underflow the current block. */ |
93 | 0 | ref_stack_pop_block(&d_stack); |
94 | 0 | } |
95 | 17.7M | dsp--; |
96 | 17.7M | dict_set_top(); |
97 | 17.7M | return 0; |
98 | 17.7M | } |
99 | | |
100 | | /* <key> <value> def - */ |
101 | | /* |
102 | | * We make this into a separate procedure because |
103 | | * the interpreter will almost always call it directly. |
104 | | */ |
105 | | int |
106 | | zop_def(i_ctx_t *i_ctx_p) |
107 | 15.7M | { |
108 | 15.7M | os_ptr op = osp; |
109 | 15.7M | os_ptr op1 = op - 1; |
110 | 15.7M | ref *pvslot; |
111 | | |
112 | 15.7M | check_op(2); |
113 | | /* The following combines a check_op(2) with a type check. */ |
114 | 15.7M | switch (r_type(op1)) { |
115 | 15.2M | case t_name: { |
116 | | /* We can use the fast single-probe lookup here. */ |
117 | 15.2M | uint nidx = name_index(imemory, op1); |
118 | 15.2M | uint htemp; |
119 | | |
120 | 15.2M | if_dict_find_name_by_index_top(nidx, htemp, pvslot) { |
121 | 195k | if (dtop_can_store(op)) |
122 | 195k | goto ra; |
123 | 195k | } |
124 | 15.0M | break; /* handle all slower cases */ |
125 | 15.2M | } |
126 | 15.0M | case t_null: |
127 | 1 | return_error(gs_error_typecheck); |
128 | 0 | case t__invalid: |
129 | 0 | return_error(gs_error_stackunderflow); |
130 | 15.7M | } |
131 | | /* |
132 | | * Combine the check for a writable top dictionary with |
133 | | * the global/local store check. See dstack.h for details. |
134 | | */ |
135 | 15.5M | if (!dtop_can_store(op)) { |
136 | 0 | check_dict_write(*dsp); |
137 | | /* |
138 | | * If the dictionary is writable, the problem must be |
139 | | * an invalid store. |
140 | | */ |
141 | 0 | return_error(gs_error_invalidaccess); |
142 | 0 | } |
143 | | /* |
144 | | * Save a level of procedure call in the common (redefinition) |
145 | | * case. With the current interfaces, we pay a double lookup |
146 | | * in the uncommon case. |
147 | | */ |
148 | 15.5M | if (dict_find(dsp, op1, &pvslot) <= 0) |
149 | 14.4M | return idict_put(dsp, op1, op); |
150 | 1.30M | ra: |
151 | 1.30M | if ((pvslot->tas.type_attrs & (&i_ctx_p->memory)->test_mask) == 0) |
152 | 1.52k | alloc_save_change(idmemory, &dsp->value.pdict->values, (ref_packed *)pvslot, "dict_put(value)"); |
153 | 1.30M | ref_assign_new_inline(pvslot,op); |
154 | | |
155 | 1.30M | return 0; |
156 | 15.5M | } |
157 | | int |
158 | | zdef(i_ctx_t *i_ctx_p) |
159 | 0 | { |
160 | 0 | int code = zop_def(i_ctx_p); |
161 | |
|
162 | 0 | if (code >= 0) { |
163 | 0 | pop(2); |
164 | 0 | } |
165 | 0 | return code; |
166 | 0 | } |
167 | | |
168 | | /* <key> load <value> */ |
169 | | static int |
170 | | zload(i_ctx_t *i_ctx_p) |
171 | 17.9M | { |
172 | 17.9M | os_ptr op = osp; |
173 | 17.9M | ref *pvalue; |
174 | | |
175 | 17.9M | check_op(1); |
176 | 17.9M | switch (r_type(op)) { |
177 | 17.9M | case t_name: |
178 | | /* Use the fast lookup. */ |
179 | 17.9M | if ((pvalue = dict_find_name(op)) == 0) |
180 | 1 | return_error(gs_error_undefined); |
181 | 17.9M | ref_assign(op, pvalue); |
182 | 17.9M | return 0; |
183 | 0 | case t_null: |
184 | 0 | return_error(gs_error_typecheck); |
185 | 0 | case t__invalid: |
186 | 0 | return_error(gs_error_stackunderflow); |
187 | 0 | default: { |
188 | | /* Use an explicit loop. */ |
189 | 0 | uint size = ref_stack_count(&d_stack); |
190 | 0 | uint i; |
191 | |
|
192 | 0 | for (i = 0; i < size; i++) { |
193 | 0 | ref *dp = ref_stack_index(&d_stack, i); |
194 | |
|
195 | 0 | if (dp == NULL) |
196 | 0 | return_error(gs_error_stackunderflow); |
197 | | |
198 | 0 | check_dict_read(*dp); |
199 | 0 | if (dict_find(dp, op, &pvalue) > 0) { |
200 | 0 | ref_assign(op, pvalue); |
201 | 0 | return 0; |
202 | 0 | } |
203 | 0 | } |
204 | 0 | return_error(gs_error_undefined); |
205 | 0 | } |
206 | 17.9M | } |
207 | 17.9M | } |
208 | | |
209 | | /* get - implemented in zgeneric.c */ |
210 | | |
211 | | /* put - implemented in zgeneric.c */ |
212 | | |
213 | | /* <dict> <key> .undef - */ |
214 | | /* <dict> <key> undef - */ |
215 | | static int |
216 | | zundef(i_ctx_t *i_ctx_p) |
217 | 1.43M | { |
218 | 1.43M | os_ptr op = osp; |
219 | 1.43M | os_ptr op1 = op - 1; |
220 | 1.43M | int code; |
221 | | |
222 | 1.43M | check_op(2); |
223 | 1.43M | check_type(*op1, t_dictionary); |
224 | 1.43M | check_dict_write(*op1); |
225 | 1.43M | code = idict_undef(op1, op); |
226 | 1.43M | if (code < 0 && code != gs_error_undefined) /* ignore undefined error */ |
227 | 0 | return code; |
228 | 1.43M | pop(2); |
229 | 1.43M | return 0; |
230 | 1.43M | } |
231 | | |
232 | | /* <dict> <key> known <bool> */ |
233 | | static int |
234 | | zknown(i_ctx_t *i_ctx_p) |
235 | 22.3M | { |
236 | 22.3M | os_ptr op = osp; |
237 | 22.3M | register os_ptr op1 = op - 1; |
238 | 22.3M | ref *pvalue; |
239 | 22.3M | int code; |
240 | | |
241 | 22.3M | check_op(2); |
242 | 22.3M | check_type(*op1, t_dictionary); |
243 | 22.3M | check_dict_read(*op1); |
244 | 22.3M | code = dict_find(op1, op, &pvalue); |
245 | 22.3M | switch (code) { |
246 | 1.01M | case gs_error_dictfull: |
247 | 1.01M | code = 0; |
248 | 22.3M | case 0: case 1: |
249 | 22.3M | break; |
250 | 0 | default: |
251 | 0 | return code; |
252 | 22.3M | } |
253 | 22.3M | make_bool(op1, code); |
254 | 22.3M | pop(1); |
255 | 22.3M | return 0; |
256 | 22.3M | } |
257 | | |
258 | | /* <key> where <dict> true */ |
259 | | /* <key> where false */ |
260 | | int |
261 | | zwhere(i_ctx_t *i_ctx_p) |
262 | 971k | { |
263 | 971k | os_ptr op = osp; |
264 | 971k | ref_stack_enum_t rsenum; |
265 | | |
266 | 971k | check_op(1); |
267 | 971k | ref_stack_enum_begin(&rsenum, &d_stack); |
268 | 971k | do { |
269 | 971k | const ref *const bot = rsenum.ptr; |
270 | 971k | const ref *pdref = bot + rsenum.size; |
271 | 971k | ref *pvalue; |
272 | 971k | int code; |
273 | | |
274 | 3.59M | while (pdref-- > bot) { |
275 | 3.11M | check_dict_read(*pdref); |
276 | 3.11M | code = dict_find(pdref, op, &pvalue); |
277 | 3.11M | if (code < 0 && code != gs_error_dictfull) |
278 | 0 | return code; |
279 | 3.11M | if (code > 0) { |
280 | 493k | push(1); |
281 | 493k | ref_assign(op - 1, pdref); |
282 | 493k | make_true(op); |
283 | 493k | return 0; |
284 | 493k | } |
285 | 3.11M | } |
286 | 971k | } while (ref_stack_enum_next(&rsenum)); |
287 | 477k | make_false(op); |
288 | 477k | return 0; |
289 | 971k | } |
290 | | |
291 | | /* copy for dictionaries -- called from zcopy in zgeneric.c. */ |
292 | | /* Only the type of *op has been checked. */ |
293 | | int |
294 | | zcopy_dict(i_ctx_t *i_ctx_p) |
295 | 136k | { |
296 | 136k | os_ptr op = osp; |
297 | 136k | os_ptr op1 = op - 1; |
298 | 136k | int code; |
299 | | |
300 | 136k | check_op(2); |
301 | 136k | check_type(*op1, t_dictionary); |
302 | 136k | check_dict_read(*op1); |
303 | 136k | check_dict_write(*op); |
304 | 136k | if (!imemory->gs_lib_ctx->dict_auto_expand && |
305 | 136k | (dict_length(op) != 0 || dict_maxlength(op) < dict_length(op1)) |
306 | 136k | ) |
307 | 0 | return_error(gs_error_rangecheck); |
308 | 136k | code = idict_copy(op1, op); |
309 | 136k | if (code < 0) |
310 | 0 | return code; |
311 | | /* |
312 | | * In Level 1 systems, we must copy the access attributes too. |
313 | | * The only possible effect this can have is to make the |
314 | | * copy read-only if the original dictionary is read-only. |
315 | | */ |
316 | 136k | if (!level2_enabled) |
317 | 136k | r_copy_attrs(dict_access_ref(op), a_write, dict_access_ref(op1)); |
318 | 136k | ref_assign(op1, op); |
319 | 136k | pop(1); |
320 | 136k | return 0; |
321 | 136k | } |
322 | | |
323 | | /* - currentdict <dict> */ |
324 | | static int |
325 | | zcurrentdict(i_ctx_t *i_ctx_p) |
326 | 4.90M | { |
327 | 4.90M | os_ptr op = osp; |
328 | | |
329 | 4.90M | push(1); |
330 | 4.90M | ref_assign(op, dsp); |
331 | 4.90M | return 0; |
332 | 4.90M | } |
333 | | |
334 | | /* - countdictstack <int> */ |
335 | | static int |
336 | | zcountdictstack(i_ctx_t *i_ctx_p) |
337 | 12.6k | { |
338 | 12.6k | os_ptr op = osp; |
339 | 12.6k | uint count = ref_stack_count(&d_stack); |
340 | | |
341 | 12.6k | push(1); |
342 | 12.6k | if (!level2_enabled) |
343 | 0 | count--; /* see dstack.h */ |
344 | 12.6k | make_int(op, count); |
345 | 12.6k | return 0; |
346 | 12.6k | } |
347 | | |
348 | | /* <array> dictstack <subarray> */ |
349 | | static int |
350 | | zdictstack(i_ctx_t *i_ctx_p) |
351 | 12.6k | { |
352 | 12.6k | os_ptr op = osp; |
353 | 12.6k | uint count = ref_stack_count(&d_stack); |
354 | | |
355 | 12.6k | check_op(1); |
356 | 12.6k | if (!level2_enabled) |
357 | 0 | count--; /* see dstack.h */ |
358 | 12.6k | if (!r_is_array(op)) |
359 | 12.6k | return_op_typecheck(op); |
360 | 12.6k | if (r_size(op) < count) |
361 | 0 | return_error(gs_error_rangecheck); |
362 | 12.6k | if (!r_has_type_attrs(op, t_array, a_write)) |
363 | 0 | return_error(gs_error_invalidaccess); |
364 | 12.6k | return ref_stack_store(&d_stack, op, count, 0, 0, true, idmemory, |
365 | 12.6k | "dictstack"); |
366 | 12.6k | } |
367 | | |
368 | | /* - cleardictstack - */ |
369 | | static int |
370 | | zcleardictstack(i_ctx_t *i_ctx_p) |
371 | 28 | { |
372 | 29 | while (zend(i_ctx_p) >= 0) |
373 | 28 | DO_NOTHING; |
374 | 28 | return 0; |
375 | 28 | } |
376 | | |
377 | | /* ------ Extensions ------ */ |
378 | | |
379 | | /* -mark- <key0> <value0> <key1> <value1> ... .dicttomark <dict> */ |
380 | | /* This is the Level 2 >> operator. */ |
381 | | static int |
382 | | zdicttomark(i_ctx_t *i_ctx_p) |
383 | 2.97M | { |
384 | 2.97M | uint count2 = ref_stack_counttomark(&o_stack); |
385 | 2.97M | ref rdict; |
386 | 2.97M | int code; |
387 | 2.97M | uint idx; |
388 | | |
389 | 2.97M | if (count2 == 0) |
390 | 3 | return_error(gs_error_unmatchedmark); |
391 | 2.97M | count2--; |
392 | 2.97M | if ((count2 & 1) != 0) |
393 | 2 | return_error(gs_error_rangecheck); |
394 | 2.97M | code = dict_create(count2 >> 1, &rdict); |
395 | 2.97M | if (code < 0) |
396 | 0 | return code; |
397 | 2.97M | if ((i_ctx_p->scanner_options & SCAN_PDF_RULES) != 0) { |
398 | 0 | for (idx = count2; idx > 0; idx -= 2) { |
399 | 0 | code = idict_put(&rdict, |
400 | 0 | ref_stack_index(&o_stack, idx - 1), |
401 | 0 | ref_stack_index(&o_stack, idx - 2)); |
402 | 0 | if (code < 0) { /* There's no way to free the dictionary -- too bad. */ |
403 | 0 | return code; |
404 | 0 | } |
405 | 0 | } |
406 | 0 | } |
407 | 2.97M | else { |
408 | | /* << /a 1 /a 2 >> => << /a 1 >>, i.e., */ |
409 | | /* we must enter the keys in top-to-bottom order. */ |
410 | 105M | for (idx = 0; idx < count2; idx += 2) { |
411 | 102M | code = idict_put(&rdict, |
412 | 102M | ref_stack_index(&o_stack, idx + 1), |
413 | 102M | ref_stack_index(&o_stack, idx)); |
414 | 102M | if (code < 0) { /* There's no way to free the dictionary -- too bad. */ |
415 | 0 | return code; |
416 | 0 | } |
417 | 102M | } |
418 | 2.97M | } |
419 | 2.97M | ref_stack_pop(&o_stack, count2); |
420 | 2.97M | ref_assign(osp, &rdict); |
421 | 2.97M | return code; |
422 | 2.97M | } |
423 | | |
424 | | /* <dict1> <dict2> .forcecopynew <dict2> */ |
425 | | /* |
426 | | * This operator is a special-purpose accelerator for use by 'restore' (see |
427 | | * gs_dps1.ps). Note that this operator does *not* require that dict2 be |
428 | | * writable. Hence it is in the same category of "dangerous" operators as |
429 | | * .forceput and .forceundef. |
430 | | */ |
431 | | static int |
432 | | zforcecopynew(i_ctx_t *i_ctx_p) |
433 | 11.2k | { |
434 | 11.2k | os_ptr op = osp; |
435 | 11.2k | os_ptr op1 = op - 1; |
436 | 11.2k | int code; |
437 | | |
438 | 11.2k | check_op(2); |
439 | 11.2k | check_type(*op1, t_dictionary); |
440 | 11.2k | check_dict_read(*op1); |
441 | 11.2k | check_type(*op, t_dictionary); |
442 | | /*check_dict_write(*op);*/ /* see above */ |
443 | | /* This is only recognized in Level 2 mode. */ |
444 | 11.2k | if (!imemory->gs_lib_ctx->dict_auto_expand) |
445 | 0 | return_error(gs_error_undefined); |
446 | 11.2k | code = idict_copy_new(op1, op); |
447 | 11.2k | if (code < 0) |
448 | 0 | return code; |
449 | 11.2k | ref_assign(op1, op); |
450 | 11.2k | pop(1); |
451 | 11.2k | return 0; |
452 | 11.2k | } |
453 | | |
454 | | /* <dict> <key> .forceundef - */ |
455 | | /* |
456 | | * This forces an "undef" even if the dictionary is not writable. |
457 | | * Like .forceput, it is meant to be used only in a few special situations, |
458 | | * and should not be accessible by name after initialization. |
459 | | */ |
460 | | static int |
461 | | zforceundef(i_ctx_t *i_ctx_p) |
462 | 4.86M | { |
463 | 4.86M | os_ptr op = osp; |
464 | | |
465 | 4.86M | check_op(2); |
466 | 4.86M | check_type(op[-1], t_dictionary); |
467 | | /* Don't check_dict_write */ |
468 | 4.86M | idict_undef(op - 1, op); /* ignore undefined error */ |
469 | 4.86M | pop(2); |
470 | 4.86M | return 0; |
471 | 4.86M | } |
472 | | |
473 | | /* <dict> <key> .knownget <value> true */ |
474 | | /* <dict> <key> .knownget false */ |
475 | | static int |
476 | | zknownget(i_ctx_t *i_ctx_p) |
477 | 53.4M | { |
478 | 53.4M | os_ptr op = osp; |
479 | 53.4M | register os_ptr op1 = op - 1; |
480 | 53.4M | ref *pvalue; |
481 | | |
482 | 53.4M | check_op(2); |
483 | 53.4M | check_type(*op1, t_dictionary); |
484 | 53.4M | check_dict_read(*op1); |
485 | 53.4M | if (dict_find(op1, op, &pvalue) <= 0) { |
486 | 10.3M | make_false(op1); |
487 | 10.3M | pop(1); |
488 | 43.0M | } else { |
489 | 43.0M | ref_assign(op1, pvalue); |
490 | 43.0M | make_true(op); |
491 | 43.0M | } |
492 | 53.4M | return 0; |
493 | 53.4M | } |
494 | | |
495 | | /* <dict> <key> .knownundef <bool> */ |
496 | | static int |
497 | | zknownundef(i_ctx_t *i_ctx_p) |
498 | 0 | { |
499 | 0 | os_ptr op = osp; |
500 | 0 | os_ptr op1 = op - 1; |
501 | 0 | int code; |
502 | |
|
503 | 0 | check_op(2); |
504 | 0 | check_type(*op1, t_dictionary); |
505 | 0 | check_dict_write(*op1); |
506 | 0 | code = idict_undef(op1, op); |
507 | 0 | make_bool(op1, code == 0); |
508 | 0 | pop(1); |
509 | 0 | return 0; |
510 | 0 | } |
511 | | |
512 | | /* <dict> <int> .setmaxlength - */ |
513 | | static int |
514 | | zsetmaxlength(i_ctx_t *i_ctx_p) |
515 | 684k | { |
516 | 684k | os_ptr op = osp; |
517 | 684k | os_ptr op1 = op - 1; |
518 | 684k | uint new_size; |
519 | 684k | int code; |
520 | | |
521 | 684k | check_op(2); |
522 | 684k | check_type(*op1, t_dictionary); |
523 | 684k | check_dict_write(*op1); |
524 | 684k | check_type(*op, t_integer); |
525 | 684k | if (op->value.intval < 0) |
526 | 0 | return_error(gs_error_rangecheck); |
527 | 684k | new_size = (uint) op->value.intval; |
528 | 684k | if (dict_length(op - 1) > new_size) |
529 | 0 | return_error(gs_error_dictfull); |
530 | 684k | code = idict_resize(op - 1, new_size); |
531 | 684k | if (code >= 0) |
532 | 684k | pop(2); |
533 | 684k | return code; |
534 | 684k | } |
535 | | |
536 | | /* ------ Initialization procedure ------ */ |
537 | | |
538 | | /* We need to split the table because of the 16-element limit. */ |
539 | | const op_def zdict1_op_defs[] = { |
540 | | {"0cleardictstack", zcleardictstack}, |
541 | | {"1begin", zbegin}, |
542 | | {"0countdictstack", zcountdictstack}, |
543 | | {"0currentdict", zcurrentdict}, |
544 | | {"2def", zdef}, |
545 | | {"1dict", zdict}, |
546 | | {"0dictstack", zdictstack}, |
547 | | {"0end", zend}, |
548 | | {"2known", zknown}, |
549 | | {"1load", zload}, |
550 | | {"1maxlength", zmaxlength}, |
551 | | {"2.undef", zundef}, /* we need this even in Level 1 */ |
552 | | {"1where", zwhere}, |
553 | | op_def_end(0) |
554 | | }; |
555 | | const op_def zdict2_op_defs[] = { |
556 | | /* Extensions */ |
557 | | {"1.dicttomark", zdicttomark}, |
558 | | {"2.forcecopynew", zforcecopynew}, |
559 | | {"2.forceundef", zforceundef}, |
560 | | {"2.knownget", zknownget}, |
561 | | {"1.knownundef", zknownundef}, |
562 | | {"2.setmaxlength", zsetmaxlength}, |
563 | | /* |
564 | | * In Level 2, >> is a synonym for .dicttomark, and undef for |
565 | | * .undef. By giving the former their own entries, they will not be |
566 | | * "eq" to .dicttomark and .undef, but that doesn't matter, since |
567 | | * we're doing this only for the sake of Adobe- compatible error |
568 | | * stacks. |
569 | | */ |
570 | | op_def_begin_level2(), |
571 | | {"1>>", zdicttomark}, |
572 | | {"2undef", zundef}, |
573 | | op_def_end(0) |
574 | | }; |