c2be1a26cd3aa5ea0fcb5851b783d080414ba63e
[p5sagit/p5-mst-13.2.git] / cmd.c
1 /* $Header: cmd.c,v 1.0.1.1 88/01/21 21:24:16 root Exp $
2  *
3  * $Log:        cmd.c,v $
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  * 
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:
389 #ifdef DEBUGGING
390             dlevel = olddlevel;
391 #endif
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 }