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