Commit | Line | Data |
36ce8bec |
1 | /* $Header: cmd.c,v 1.0.1.1 88/01/21 21:24:16 root Exp $ |
8d063cd8 |
2 | * |
3 | * $Log: cmd.c,v $ |
36ce8bec |
4 | * Revision 1.0.1.1 88/01/21 21:24:16 root |
5 | * The redo cmd got a segmentation fault because trace context stack overflowed. |
6 | * |
8d063cd8 |
7 | * Revision 1.0 87/12/18 13:04:51 root |
8 | * Initial revision |
9 | * |
10 | */ |
11 | |
12 | #include "handy.h" |
13 | #include "EXTERN.h" |
14 | #include "search.h" |
15 | #include "util.h" |
16 | #include "perl.h" |
17 | |
18 | static STR str_chop; |
19 | |
20 | /* This is the main command loop. We try to spend as much time in this loop |
21 | * as possible, so lots of optimizations do their activities in here. This |
22 | * means things get a little sloppy. |
23 | */ |
24 | |
25 | STR * |
26 | cmd_exec(cmd) |
27 | register CMD *cmd; |
28 | { |
29 | SPAT *oldspat; |
30 | #ifdef DEBUGGING |
31 | int olddlevel; |
32 | int entdlevel; |
33 | #endif |
34 | register STR *retstr; |
35 | register char *tmps; |
36 | register int cmdflags; |
37 | register bool match; |
38 | register char *go_to = goto_targ; |
39 | ARG *arg; |
40 | FILE *fp; |
41 | |
42 | retstr = &str_no; |
43 | #ifdef DEBUGGING |
44 | entdlevel = dlevel; |
45 | #endif |
46 | tail_recursion_entry: |
47 | #ifdef DEBUGGING |
48 | dlevel = entdlevel; |
49 | #endif |
50 | if (cmd == Nullcmd) |
51 | return retstr; |
52 | cmdflags = cmd->c_flags; /* hopefully load register */ |
53 | if (go_to) { |
54 | if (cmd->c_label && strEQ(go_to,cmd->c_label)) |
55 | goto_targ = go_to = Nullch; /* here at last */ |
56 | else { |
57 | switch (cmd->c_type) { |
58 | case C_IF: |
59 | oldspat = curspat; |
60 | #ifdef DEBUGGING |
61 | olddlevel = dlevel; |
62 | #endif |
63 | retstr = &str_yes; |
64 | if (cmd->ucmd.ccmd.cc_true) { |
65 | #ifdef DEBUGGING |
66 | debname[dlevel] = 't'; |
67 | debdelim[dlevel++] = '_'; |
68 | #endif |
69 | retstr = cmd_exec(cmd->ucmd.ccmd.cc_true); |
70 | } |
71 | if (!goto_targ) { |
72 | go_to = Nullch; |
73 | } else { |
74 | retstr = &str_no; |
75 | if (cmd->ucmd.ccmd.cc_alt) { |
76 | #ifdef DEBUGGING |
77 | debname[dlevel] = 'e'; |
78 | debdelim[dlevel++] = '_'; |
79 | #endif |
80 | retstr = cmd_exec(cmd->ucmd.ccmd.cc_alt); |
81 | } |
82 | } |
83 | if (!goto_targ) |
84 | go_to = Nullch; |
85 | curspat = oldspat; |
86 | #ifdef DEBUGGING |
87 | dlevel = olddlevel; |
88 | #endif |
89 | break; |
90 | case C_BLOCK: |
91 | case C_WHILE: |
92 | if (!(cmdflags & CF_ONCE)) { |
93 | cmdflags |= CF_ONCE; |
94 | loop_ptr++; |
95 | loop_stack[loop_ptr].loop_label = cmd->c_label; |
96 | #ifdef DEBUGGING |
97 | if (debug & 4) { |
98 | deb("(Pushing label #%d %s)\n", |
99 | loop_ptr,cmd->c_label); |
100 | } |
101 | #endif |
102 | } |
103 | switch (setjmp(loop_stack[loop_ptr].loop_env)) { |
104 | case O_LAST: /* not done unless go_to found */ |
105 | go_to = Nullch; |
106 | retstr = &str_no; |
107 | #ifdef DEBUGGING |
108 | olddlevel = dlevel; |
109 | #endif |
110 | curspat = oldspat; |
111 | #ifdef DEBUGGING |
112 | if (debug & 4) { |
113 | deb("(Popping label #%d %s)\n",loop_ptr, |
114 | loop_stack[loop_ptr].loop_label); |
115 | } |
116 | #endif |
117 | loop_ptr--; |
118 | cmd = cmd->c_next; |
119 | goto tail_recursion_entry; |
120 | case O_NEXT: /* not done unless go_to found */ |
121 | go_to = Nullch; |
122 | goto next_iter; |
123 | case O_REDO: /* not done unless go_to found */ |
124 | go_to = Nullch; |
125 | goto doit; |
126 | } |
127 | oldspat = curspat; |
128 | #ifdef DEBUGGING |
129 | olddlevel = dlevel; |
130 | #endif |
131 | if (cmd->ucmd.ccmd.cc_true) { |
132 | #ifdef DEBUGGING |
133 | debname[dlevel] = 't'; |
134 | debdelim[dlevel++] = '_'; |
135 | #endif |
136 | cmd_exec(cmd->ucmd.ccmd.cc_true); |
137 | } |
138 | if (!goto_targ) { |
139 | go_to = Nullch; |
140 | goto next_iter; |
141 | } |
142 | #ifdef DEBUGGING |
143 | dlevel = olddlevel; |
144 | #endif |
145 | if (cmd->ucmd.ccmd.cc_alt) { |
146 | #ifdef DEBUGGING |
147 | debname[dlevel] = 'a'; |
148 | debdelim[dlevel++] = '_'; |
149 | #endif |
150 | cmd_exec(cmd->ucmd.ccmd.cc_alt); |
151 | } |
152 | if (goto_targ) |
153 | break; |
154 | go_to = Nullch; |
155 | goto finish_while; |
156 | } |
157 | cmd = cmd->c_next; |
158 | if (cmd && cmd->c_head == cmd) /* reached end of while loop */ |
159 | return retstr; /* targ isn't in this block */ |
160 | goto tail_recursion_entry; |
161 | } |
162 | } |
163 | |
164 | until_loop: |
165 | |
166 | #ifdef DEBUGGING |
167 | if (debug & 2) { |
168 | deb("%s (%lx) r%lx t%lx a%lx n%lx cs%lx\n", |
169 | cmdname[cmd->c_type],cmd,cmd->c_expr, |
170 | cmd->ucmd.ccmd.cc_true,cmd->ucmd.ccmd.cc_alt,cmd->c_next,curspat); |
171 | } |
172 | debname[dlevel] = cmdname[cmd->c_type][0]; |
173 | debdelim[dlevel++] = '!'; |
174 | #endif |
175 | while (tmps_max >= 0) /* clean up after last eval */ |
176 | str_free(tmps_list[tmps_max--]); |
177 | |
178 | /* Here is some common optimization */ |
179 | |
180 | if (cmdflags & CF_COND) { |
181 | switch (cmdflags & CF_OPTIMIZE) { |
182 | |
183 | case CFT_FALSE: |
184 | retstr = cmd->c_first; |
185 | match = FALSE; |
186 | if (cmdflags & CF_NESURE) |
187 | goto maybe; |
188 | break; |
189 | case CFT_TRUE: |
190 | retstr = cmd->c_first; |
191 | match = TRUE; |
192 | if (cmdflags & CF_EQSURE) |
193 | goto flipmaybe; |
194 | break; |
195 | |
196 | case CFT_REG: |
197 | retstr = STAB_STR(cmd->c_stab); |
198 | match = str_true(retstr); /* => retstr = retstr, c2 should fix */ |
199 | if (cmdflags & (match ? CF_EQSURE : CF_NESURE)) |
200 | goto flipmaybe; |
201 | break; |
202 | |
203 | case CFT_ANCHOR: /* /^pat/ optimization */ |
204 | if (multiline) { |
205 | if (*cmd->c_first->str_ptr && !(cmdflags & CF_EQSURE)) |
206 | goto scanner; /* just unanchor it */ |
207 | else |
208 | break; /* must evaluate */ |
209 | } |
210 | /* FALL THROUGH */ |
211 | case CFT_STROP: /* string op optimization */ |
212 | retstr = STAB_STR(cmd->c_stab); |
213 | if (*cmd->c_first->str_ptr == *str_get(retstr) && |
214 | strnEQ(cmd->c_first->str_ptr, str_get(retstr), |
215 | cmd->c_flen) ) { |
216 | if (cmdflags & CF_EQSURE) { |
217 | match = !(cmdflags & CF_FIRSTNEG); |
218 | retstr = &str_yes; |
219 | goto flipmaybe; |
220 | } |
221 | } |
222 | else if (cmdflags & CF_NESURE) { |
223 | match = cmdflags & CF_FIRSTNEG; |
224 | retstr = &str_no; |
225 | goto flipmaybe; |
226 | } |
227 | break; /* must evaluate */ |
228 | |
229 | case CFT_SCAN: /* non-anchored search */ |
230 | scanner: |
231 | retstr = STAB_STR(cmd->c_stab); |
232 | if (instr(str_get(retstr),cmd->c_first->str_ptr)) { |
233 | if (cmdflags & CF_EQSURE) { |
234 | match = !(cmdflags & CF_FIRSTNEG); |
235 | retstr = &str_yes; |
236 | goto flipmaybe; |
237 | } |
238 | } |
239 | else if (cmdflags & CF_NESURE) { |
240 | match = cmdflags & CF_FIRSTNEG; |
241 | retstr = &str_no; |
242 | goto flipmaybe; |
243 | } |
244 | break; /* must evaluate */ |
245 | |
246 | case CFT_GETS: /* really a while (<file>) */ |
247 | last_in_stab = cmd->c_stab; |
248 | fp = last_in_stab->stab_io->fp; |
249 | retstr = defstab->stab_val; |
250 | if (fp && str_gets(retstr, fp)) { |
251 | last_in_stab->stab_io->lines++; |
252 | match = TRUE; |
253 | } |
254 | else if (last_in_stab->stab_io->flags & IOF_ARGV) |
255 | goto doeval; /* doesn't necessarily count as EOF yet */ |
256 | else { |
257 | retstr = &str_no; |
258 | match = FALSE; |
259 | } |
260 | goto flipmaybe; |
261 | case CFT_EVAL: |
262 | break; |
263 | case CFT_UNFLIP: |
264 | retstr = eval(cmd->c_expr,Null(char***)); |
265 | match = str_true(retstr); |
266 | if (cmd->c_expr->arg_type == O_FLIP) /* undid itself? */ |
267 | cmdflags = copyopt(cmd,cmd->c_expr[3].arg_ptr.arg_cmd); |
268 | goto maybe; |
269 | case CFT_CHOP: |
270 | retstr = cmd->c_stab->stab_val; |
271 | match = (retstr->str_cur != 0); |
272 | tmps = str_get(retstr); |
273 | tmps += retstr->str_cur - match; |
274 | str_set(&str_chop,tmps); |
275 | *tmps = '\0'; |
276 | retstr->str_nok = 0; |
277 | retstr->str_cur = tmps - retstr->str_ptr; |
278 | retstr = &str_chop; |
279 | goto flipmaybe; |
280 | } |
281 | |
282 | /* we have tried to make this normal case as abnormal as possible */ |
283 | |
284 | doeval: |
285 | retstr = eval(cmd->c_expr,Null(char***)); |
286 | match = str_true(retstr); |
287 | goto maybe; |
288 | |
289 | /* if flipflop was true, flop it */ |
290 | |
291 | flipmaybe: |
292 | if (match && cmdflags & CF_FLIP) { |
293 | if (cmd->c_expr->arg_type == O_FLOP) { /* currently toggled? */ |
294 | retstr = eval(cmd->c_expr,Null(char***)); /* let eval undo it */ |
295 | cmdflags = copyopt(cmd,cmd->c_expr[3].arg_ptr.arg_cmd); |
296 | } |
297 | else { |
298 | retstr = eval(cmd->c_expr,Null(char***)); /* let eval do it */ |
299 | if (cmd->c_expr->arg_type == O_FLOP) /* still toggled? */ |
300 | cmdflags = copyopt(cmd,cmd->c_expr[4].arg_ptr.arg_cmd); |
301 | } |
302 | } |
303 | else if (cmdflags & CF_FLIP) { |
304 | if (cmd->c_expr->arg_type == O_FLOP) { /* currently toggled? */ |
305 | match = TRUE; /* force on */ |
306 | } |
307 | } |
308 | |
309 | /* at this point, match says whether our expression was true */ |
310 | |
311 | maybe: |
312 | if (cmdflags & CF_INVERT) |
313 | match = !match; |
314 | if (!match && cmd->c_type != C_IF) { |
315 | cmd = cmd->c_next; |
316 | goto tail_recursion_entry; |
317 | } |
318 | } |
319 | |
320 | /* now to do the actual command, if any */ |
321 | |
322 | switch (cmd->c_type) { |
323 | case C_NULL: |
324 | fatal("panic: cmd_exec\n"); |
325 | case C_EXPR: /* evaluated for side effects */ |
326 | if (cmd->ucmd.acmd.ac_expr) { /* more to do? */ |
327 | retstr = eval(cmd->ucmd.acmd.ac_expr,Null(char***)); |
328 | } |
329 | break; |
330 | case C_IF: |
331 | oldspat = curspat; |
332 | #ifdef DEBUGGING |
333 | olddlevel = dlevel; |
334 | #endif |
335 | if (match) { |
336 | retstr = &str_yes; |
337 | if (cmd->ucmd.ccmd.cc_true) { |
338 | #ifdef DEBUGGING |
339 | debname[dlevel] = 't'; |
340 | debdelim[dlevel++] = '_'; |
341 | #endif |
342 | retstr = cmd_exec(cmd->ucmd.ccmd.cc_true); |
343 | } |
344 | } |
345 | else { |
346 | retstr = &str_no; |
347 | if (cmd->ucmd.ccmd.cc_alt) { |
348 | #ifdef DEBUGGING |
349 | debname[dlevel] = 'e'; |
350 | debdelim[dlevel++] = '_'; |
351 | #endif |
352 | retstr = cmd_exec(cmd->ucmd.ccmd.cc_alt); |
353 | } |
354 | } |
355 | curspat = oldspat; |
356 | #ifdef DEBUGGING |
357 | dlevel = olddlevel; |
358 | #endif |
359 | break; |
360 | case C_BLOCK: |
361 | case C_WHILE: |
362 | if (!(cmdflags & CF_ONCE)) { /* first time through here? */ |
363 | cmdflags |= CF_ONCE; |
364 | loop_ptr++; |
365 | loop_stack[loop_ptr].loop_label = cmd->c_label; |
366 | #ifdef DEBUGGING |
367 | if (debug & 4) { |
368 | deb("(Pushing label #%d %s)\n", |
369 | loop_ptr,cmd->c_label); |
370 | } |
371 | #endif |
372 | } |
373 | switch (setjmp(loop_stack[loop_ptr].loop_env)) { |
374 | case O_LAST: |
375 | retstr = &str_no; |
376 | curspat = oldspat; |
377 | #ifdef DEBUGGING |
378 | if (debug & 4) { |
379 | deb("(Popping label #%d %s)\n",loop_ptr, |
380 | loop_stack[loop_ptr].loop_label); |
381 | } |
382 | #endif |
383 | loop_ptr--; |
384 | cmd = cmd->c_next; |
385 | goto tail_recursion_entry; |
386 | case O_NEXT: |
387 | goto next_iter; |
388 | case O_REDO: |
36ce8bec |
389 | #ifdef DEBUGGING |
390 | dlevel = olddlevel; |
391 | #endif |
8d063cd8 |
392 | goto doit; |
393 | } |
394 | oldspat = curspat; |
395 | #ifdef DEBUGGING |
396 | olddlevel = dlevel; |
397 | #endif |
398 | doit: |
399 | if (cmd->ucmd.ccmd.cc_true) { |
400 | #ifdef DEBUGGING |
401 | debname[dlevel] = 't'; |
402 | debdelim[dlevel++] = '_'; |
403 | #endif |
404 | cmd_exec(cmd->ucmd.ccmd.cc_true); |
405 | } |
406 | /* actually, this spot is never reached anymore since the above |
407 | * cmd_exec() returns through longjmp(). Hooray for structure. |
408 | */ |
409 | next_iter: |
410 | #ifdef DEBUGGING |
411 | dlevel = olddlevel; |
412 | #endif |
413 | if (cmd->ucmd.ccmd.cc_alt) { |
414 | #ifdef DEBUGGING |
415 | debname[dlevel] = 'a'; |
416 | debdelim[dlevel++] = '_'; |
417 | #endif |
418 | cmd_exec(cmd->ucmd.ccmd.cc_alt); |
419 | } |
420 | finish_while: |
421 | curspat = oldspat; |
422 | #ifdef DEBUGGING |
423 | dlevel = olddlevel - 1; |
424 | #endif |
425 | if (cmd->c_type != C_BLOCK) |
426 | goto until_loop; /* go back and evaluate conditional again */ |
427 | } |
428 | if (cmdflags & CF_LOOP) { |
429 | cmdflags |= CF_COND; /* now test the condition */ |
430 | goto until_loop; |
431 | } |
432 | cmd = cmd->c_next; |
433 | goto tail_recursion_entry; |
434 | } |
435 | |
436 | #ifdef DEBUGGING |
437 | /*VARARGS1*/ |
438 | deb(pat,a1,a2,a3,a4,a5,a6,a7,a8) |
439 | char *pat; |
440 | { |
441 | register int i; |
442 | |
443 | for (i=0; i<dlevel; i++) |
444 | fprintf(stderr,"%c%c ",debname[i],debdelim[i]); |
445 | fprintf(stderr,pat,a1,a2,a3,a4,a5,a6,a7,a8); |
446 | } |
447 | #endif |
448 | |
449 | copyopt(cmd,which) |
450 | register CMD *cmd; |
451 | register CMD *which; |
452 | { |
453 | cmd->c_flags &= CF_ONCE|CF_COND|CF_LOOP; |
454 | cmd->c_flags |= which->c_flags; |
455 | cmd->c_first = which->c_first; |
456 | cmd->c_flen = which->c_flen; |
457 | cmd->c_stab = which->c_stab; |
458 | return cmd->c_flags; |
459 | } |