Commit | Line | Data |
---|---|---|
3926782b WJ |
1 | /* Copyright (C) 1989, 1992 Aladdin Enterprises. All rights reserved. |
2 | Distributed by Free Software Foundation, Inc. | |
3 | ||
4 | This file is part of Ghostscript. | |
5 | ||
6 | Ghostscript is distributed in the hope that it will be useful, but | |
7 | WITHOUT ANY WARRANTY. No author or distributor accepts responsibility | |
8 | to anyone for the consequences of using it or for whether it serves any | |
9 | particular purpose or works at all, unless he says so in writing. Refer | |
10 | to the Ghostscript General Public License for full details. | |
11 | ||
12 | Everyone is granted permission to copy, modify and redistribute | |
13 | Ghostscript, but only under the conditions described in the Ghostscript | |
14 | General Public License. A copy of this license is supposed to have been | |
15 | given to you along with Ghostscript so you can know your rights and | |
16 | responsibilities. It should be in a file named COPYING. Among other | |
17 | things, the copyright notice and this notice must be preserved on all | |
18 | copies. */ | |
19 | ||
20 | /* zcontrol.c */ | |
21 | /* Control operators for Ghostscript */ | |
22 | #include "ghost.h" | |
23 | #include "errors.h" | |
24 | #include "oper.h" | |
25 | #include "estack.h" | |
26 | #include "iutil.h" | |
27 | #include "store.h" | |
28 | ||
29 | /* Export the index of the 'for' operator */ | |
30 | /* for the transfer function mapper in zcolor.c. */ | |
31 | int i_zfor; | |
32 | ||
33 | /* Check for updating the currentfile cache. */ | |
34 | #define esfile_check(ep)\ | |
35 | if ( r_has_type_attrs(ep, t_file, a_executable) ) esfile = 0 | |
36 | ||
37 | /* Forward references */ | |
38 | private es_ptr find_stopped(P0()); | |
39 | ||
40 | /* exec */ | |
41 | int | |
42 | zexec(register os_ptr op) | |
43 | { check_op(1); | |
44 | check_estack(1); | |
45 | ++esp; | |
46 | ref_assign(esp, op); | |
47 | esfile_check(esp); | |
48 | pop(1); | |
49 | return o_push_estack; | |
50 | } | |
51 | ||
52 | /* if */ | |
53 | int | |
54 | zif(register os_ptr op) | |
55 | { check_type(op[-1], t_boolean); | |
56 | if ( op[-1].value.index ) /* true */ | |
57 | { check_estack(1); | |
58 | ++esp; | |
59 | ref_assign(esp, op); | |
60 | esfile_check(esp); | |
61 | } | |
62 | pop(2); | |
63 | return o_push_estack; | |
64 | } | |
65 | ||
66 | /* ifelse */ | |
67 | int | |
68 | zifelse(register os_ptr op) | |
69 | { check_type(op[-2], t_boolean); | |
70 | check_estack(1); | |
71 | ++esp; | |
72 | if ( op[-2].value.index ) | |
73 | { ref_assign(esp, op - 1); | |
74 | } | |
75 | else | |
76 | { ref_assign(esp, op); | |
77 | } | |
78 | esfile_check(esp); | |
79 | pop(3); | |
80 | return o_push_estack; | |
81 | } | |
82 | ||
83 | /* for */ | |
84 | private int | |
85 | for_pos_int_continue(P1(os_ptr)), | |
86 | i_for_pos_int_continue, | |
87 | for_neg_int_continue(P1(os_ptr)), | |
88 | i_for_neg_int_continue, | |
89 | for_real_continue(P1(os_ptr)), | |
90 | i_for_real_continue; | |
91 | int | |
92 | zfor(register os_ptr op) | |
93 | { int code; | |
94 | float params[3]; | |
95 | register es_ptr ep; | |
96 | check_proc(*op); | |
97 | if ( r_has_type(op - 1, t_integer) && | |
98 | r_has_type(op - 2, t_integer) && | |
99 | r_has_type(op - 3, t_integer) | |
100 | ) | |
101 | code = 7; | |
102 | else if ( (code = num_params(op - 1, 3, params)) < 0 ) | |
103 | return code; | |
104 | check_estack(7); | |
105 | /* Push a mark, the control variable, the initial value, */ | |
106 | /* the increment, the limit, and the procedure, */ | |
107 | /* and invoke the continuation operator. */ | |
108 | mark_estack(es_for); | |
109 | ep = esp += 5; | |
110 | if ( (code & 3) == 3 ) /* initial & increment are ints */ | |
111 | { ep[-4] = op[-3]; | |
112 | ep[-3] = op[-2]; | |
113 | if ( code == 7 ) | |
114 | ep[-2] = op[-1]; | |
115 | else | |
116 | make_int(ep - 2, (long)params[2]); | |
117 | if ( ep[-3].value.intval >= 0 ) | |
118 | make_op_estack(ep, for_pos_int_continue, i_for_pos_int_continue); | |
119 | else | |
120 | make_op_estack(ep, for_neg_int_continue, i_for_neg_int_continue); | |
121 | } | |
122 | else | |
123 | { make_real(ep - 4, params[0]); | |
124 | make_real(ep - 3, params[1]); | |
125 | make_real(ep - 2, params[2]); | |
126 | make_op_estack(ep, for_real_continue, i_for_real_continue); | |
127 | } | |
128 | ep[-1] = *op; | |
129 | pop(4); | |
130 | return o_push_estack; | |
131 | } | |
132 | /* Continuation operators for for, separate for positive integer, */ | |
133 | /* negative integer, and real. */ | |
134 | /* Execution stack contains mark, control variable, increment, */ | |
135 | /* limit, and procedure (procedure is topmost.) */ | |
136 | /* The continuation operator is just above the top of the e-stack. */ | |
137 | /* Continuation operator for positive integers. */ | |
138 | private int | |
139 | for_pos_int_continue(register os_ptr op) | |
140 | { register es_ptr ep = esp; | |
141 | long var = ep[-3].value.intval; | |
142 | if ( var > ep[-1].value.intval ) | |
143 | { esp -= 5; /* pop everything */ | |
144 | return o_pop_estack; | |
145 | } | |
146 | push(1); | |
147 | make_int(op, var); | |
148 | ep[-3].value.intval = var + ep[-2].value.intval; | |
149 | ref_assign(ep + 2, ep); /* saved proc */ | |
150 | esp = ep + 2; | |
151 | return o_push_estack; | |
152 | } | |
153 | /* Continuation operator for negative integers. */ | |
154 | private int | |
155 | for_neg_int_continue(register os_ptr op) | |
156 | { register es_ptr ep = esp; | |
157 | long var = ep[-3].value.intval; | |
158 | if ( var < ep[-1].value.intval ) | |
159 | { esp -= 5; /* pop everything */ | |
160 | return o_pop_estack; | |
161 | } | |
162 | push(1); | |
163 | make_int(op, var); | |
164 | ep[-3].value.intval = var + ep[-2].value.intval; | |
165 | ref_assign(ep + 2, ep); /* saved proc */ | |
166 | esp = ep + 2; | |
167 | return o_push_estack; | |
168 | } | |
169 | /* Continuation operator for reals. */ | |
170 | private int | |
171 | for_real_continue(register os_ptr op) | |
172 | { es_ptr ep = esp; | |
173 | float var = ep[-3].value.realval; | |
174 | float incr = ep[-2].value.realval; | |
175 | if ( incr >= 0 ? (var > ep[-1].value.realval) : | |
176 | (var < ep[-1].value.realval) ) | |
177 | { esp -= 5; /* pop everything */ | |
178 | return o_pop_estack; | |
179 | } | |
180 | push(1); | |
181 | ref_assign(op, ep - 3); | |
182 | ep[-3].value.realval = var + incr; | |
183 | esp = ep + 2; | |
184 | ref_assign(ep + 2, ep); /* saved proc */ | |
185 | return o_push_estack; | |
186 | } | |
187 | ||
188 | /* repeat */ | |
189 | private int repeat_continue(P1(os_ptr)); | |
190 | private int i_repeat_continue; | |
191 | int | |
192 | zrepeat(register os_ptr op) | |
193 | { check_type(op[-1], t_integer); | |
194 | check_proc(*op); | |
195 | if ( op[-1].value.intval < 0 ) return e_rangecheck; | |
196 | check_estack(5); | |
197 | /* Push a mark, the count, and the procedure, and invoke */ | |
198 | /* the continuation operator. */ | |
199 | mark_estack(es_for); | |
200 | *++esp = op[-1]; | |
201 | *++esp = *op; | |
202 | pop(2); | |
203 | return repeat_continue(op - 2); | |
204 | } | |
205 | /* Continuation operator for repeat */ | |
206 | private int | |
207 | repeat_continue(register os_ptr op) | |
208 | { es_ptr ep = esp; /* saved proc */ | |
209 | if ( --(ep[-1].value.intval) >= 0 ) /* continue */ | |
210 | { push_op_estack(repeat_continue, i_repeat_continue); /* push continuation */ | |
211 | ++esp; | |
212 | ref_assign(esp, ep); | |
213 | return o_push_estack; | |
214 | } | |
215 | else /* done */ | |
216 | { esp -= 3; /* pop mark, count, proc */ | |
217 | return o_pop_estack; | |
218 | } | |
219 | } | |
220 | ||
221 | /* loop */ | |
222 | private int loop_continue(P1(os_ptr)); | |
223 | private int i_loop_continue; | |
224 | int | |
225 | zloop(register os_ptr op) | |
226 | { check_proc(*op); | |
227 | check_estack(4); | |
228 | /* Push a mark and the procedure, and invoke */ | |
229 | /* the continuation operator. */ | |
230 | mark_estack(es_for); | |
231 | *++esp = *op; | |
232 | pop(1); | |
233 | return loop_continue(op - 1); | |
234 | } | |
235 | /* Continuation operator for loop */ | |
236 | private int | |
237 | loop_continue(register os_ptr op) | |
238 | { register es_ptr ep = esp; /* saved proc */ | |
239 | make_op_estack(ep + 1, loop_continue, i_loop_continue); /* push continuation */ | |
240 | ref_assign(ep + 2, ep); | |
241 | esp = ep + 2; | |
242 | return o_push_estack; | |
243 | } | |
244 | ||
245 | /* exit */ | |
246 | int | |
247 | zexit(register os_ptr op) | |
248 | { es_ptr ep = esp; | |
249 | esfile = 0; /* be lazy, just clear the cache */ | |
250 | while ( ep >= esbot ) | |
251 | { if ( r_has_type(ep, t_null) ) /* control mark */ | |
252 | switch ( (ep--)->value.index ) | |
253 | { | |
254 | case es_for: esp = ep; return o_pop_estack; | |
255 | case es_stopped: return e_invalidexit; /* not a loop */ | |
256 | } | |
257 | else | |
258 | ep--; | |
259 | } | |
260 | /* Return e_invalidexit if there is no mark at all. */ | |
261 | /* This is different from PostScript, which aborts. */ | |
262 | /* It shouldn't matter in practice. */ | |
263 | return e_invalidexit; | |
264 | } | |
265 | ||
266 | /* stop */ | |
267 | int | |
268 | zstop(register os_ptr op) | |
269 | { es_ptr ep = find_stopped(); | |
270 | esfile = 0; /* be lazy, just clear the cache */ | |
271 | if ( ep ) | |
272 | { esp = ep - 1; | |
273 | push(1); | |
274 | make_bool(op, 1); | |
275 | return o_pop_estack; | |
276 | } | |
277 | /* Return e_invalidexit if there is no mark at all. */ | |
278 | /* This is different from PostScript, which aborts. */ | |
279 | /* It shouldn't matter in practice. */ | |
280 | return e_invalidexit; | |
281 | } | |
282 | ||
283 | /* stopped */ | |
284 | int | |
285 | zstopped(register os_ptr op) | |
286 | { check_op(1); | |
287 | /* Mark the execution stack, and push a false in case */ | |
288 | /* control returns normally. */ | |
289 | check_estack(3); | |
290 | mark_estack(es_stopped); | |
291 | ++esp; make_false(esp); | |
292 | *++esp = *op; /* execute the operand */ | |
293 | esfile_check(esp); | |
294 | pop(1); | |
295 | return o_push_estack; | |
296 | } | |
297 | ||
298 | /* .instopped */ | |
299 | int | |
300 | zinstopped(register os_ptr op) | |
301 | { push(1); | |
302 | make_bool(op, find_stopped() != 0); | |
303 | return 0; | |
304 | } | |
305 | ||
306 | /* countexecstack */ | |
307 | int | |
308 | zcountexecstack(register os_ptr op) | |
309 | { push(1); | |
310 | make_int(op, esp - esbot + 1); | |
311 | return 0; | |
312 | } | |
313 | ||
314 | /* execstack */ | |
315 | private int execstack_continue(P1(os_ptr)); | |
316 | private int i_execstack_continue; | |
317 | int | |
318 | zexecstack(register os_ptr op) | |
319 | { /* We can't do this directly, because the interpreter */ | |
320 | /* might have cached some state. To force the interpreter */ | |
321 | /* to update the stored state, we push a continuation on */ | |
322 | /* the exec stack; the continuation is executed immediately, */ | |
323 | /* and does the actual transfer. */ | |
324 | int depth = esp - esbot + 1; | |
325 | check_write_type(*op, t_array); | |
326 | if ( depth > r_size(op) ) return e_rangecheck; | |
327 | check_estack(1); | |
328 | r_set_size(op, depth); | |
329 | push_op_estack(execstack_continue, i_execstack_continue); | |
330 | return o_push_estack; | |
331 | } | |
332 | /* Continuation operator to do the actual transfer */ | |
333 | private int | |
334 | execstack_continue(register os_ptr op) | |
335 | { int depth = r_size(op); /* was set above */ | |
336 | refcpy_to_old(op->value.refs, esbot, depth, "execstack"); | |
337 | return 0; | |
338 | } | |
339 | ||
340 | /* .quit */ | |
341 | int | |
342 | zquit(register os_ptr op) | |
343 | { check_type(*op, t_integer); | |
344 | gs_exit((int)op->value.intval); | |
345 | /* gs_exit doesn't return, but just in case a miracle happens.... */ | |
346 | pop(1); | |
347 | return 0; | |
348 | } | |
349 | ||
350 | /* ------ Initialization procedure ------ */ | |
351 | ||
352 | op_def zcontrol_op_defs[] = { | |
353 | {"0countexecstack", zcountexecstack}, | |
354 | {"1exec", zexec}, | |
355 | {"0execstack", zexecstack}, | |
356 | {"0exit", zexit}, | |
357 | {"2if", zif}, | |
358 | {"3ifelse", zifelse}, | |
359 | {"0.instopped", zinstopped}, | |
360 | {"4for", zfor, &i_zfor}, | |
361 | {"1loop", zloop}, | |
362 | {"1.quit", zquit}, | |
363 | {"2repeat", zrepeat}, | |
364 | {"0stop", zstop}, | |
365 | {"1stopped", zstopped}, | |
366 | /* Internal operators */ | |
367 | {"0%execstack_continue", execstack_continue, &i_execstack_continue}, | |
368 | {"0%for_pos_int_continue", for_pos_int_continue, &i_for_pos_int_continue}, | |
369 | {"0%for_neg_int_continue", for_neg_int_continue, &i_for_neg_int_continue}, | |
370 | {"0%for_real_continue", for_real_continue, &i_for_real_continue}, | |
371 | {"0%loop_continue", loop_continue, &i_loop_continue}, | |
372 | {"0%repeat_continue", repeat_continue, &i_repeat_continue}, | |
373 | op_def_end(0) | |
374 | }; | |
375 | ||
376 | /* Internal routines */ | |
377 | ||
378 | /* Find a `stopped' mark on the e-stack. */ | |
379 | /* Return the e-stack pointer or 0. */ | |
380 | private es_ptr | |
381 | find_stopped() | |
382 | { register es_ptr ep; | |
383 | for ( ep = esp; ep >= esbot; --ep ) | |
384 | if ( r_has_type(ep, t_null) && ep->value.index == es_stopped ) | |
385 | return ep; | |
386 | return 0; | |
387 | } |