386BSD 0.1 development
[unix-history] / usr / othersrc / public / ghostscript-2.4.1 / zcontrol.c
CommitLineData
3926782b
WJ
1/* Copyright (C) 1989, 1992 Aladdin Enterprises. All rights reserved.
2 Distributed by Free Software Foundation, Inc.
3
4This file is part of Ghostscript.
5
6Ghostscript is distributed in the hope that it will be useful, but
7WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
8to anyone for the consequences of using it or for whether it serves any
9particular purpose or works at all, unless he says so in writing. Refer
10to the Ghostscript General Public License for full details.
11
12Everyone is granted permission to copy, modify and redistribute
13Ghostscript, but only under the conditions described in the Ghostscript
14General Public License. A copy of this license is supposed to have been
15given to you along with Ghostscript so you can know your rights and
16responsibilities. It should be in a file named COPYING. Among other
17things, the copyright notice and this notice must be preserved on all
18copies. */
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. */
31int 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 */
38private es_ptr find_stopped(P0());
39
40/* exec */
41int
42zexec(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 */
53int
54zif(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 */
67int
68zifelse(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 */
84private 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;
91int
92zfor(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. */
138private int
139for_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. */
154private int
155for_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. */
170private int
171for_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 */
189private int repeat_continue(P1(os_ptr));
190private int i_repeat_continue;
191int
192zrepeat(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 */
206private int
207repeat_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 */
222private int loop_continue(P1(os_ptr));
223private int i_loop_continue;
224int
225zloop(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 */
236private int
237loop_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 */
246int
247zexit(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 */
267int
268zstop(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 */
284int
285zstopped(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 */
299int
300zinstopped(register os_ptr op)
301{ push(1);
302 make_bool(op, find_stopped() != 0);
303 return 0;
304}
305
306/* countexecstack */
307int
308zcountexecstack(register os_ptr op)
309{ push(1);
310 make_int(op, esp - esbot + 1);
311 return 0;
312}
313
314/* execstack */
315private int execstack_continue(P1(os_ptr));
316private int i_execstack_continue;
317int
318zexecstack(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 */
333private int
334execstack_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 */
341int
342zquit(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
352op_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. */
380private es_ptr
381find_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}