xref: /csrg-svn/old/dbx/eval.c (revision 14674)
1 /* Copyright (c) 1982 Regents of the University of California */
2 
3 static char sccsid[] = "@(#)eval.c 1.10 08/17/83";
4 
5 /*
6  * Tree evaluation.
7  */
8 
9 #include "defs.h"
10 #include "tree.h"
11 #include "operators.h"
12 #include "eval.h"
13 #include "events.h"
14 #include "symbols.h"
15 #include "scanner.h"
16 #include "source.h"
17 #include "object.h"
18 #include "mappings.h"
19 #include "process.h"
20 #include "machine.h"
21 #include <signal.h>
22 
23 #ifndef public
24 
25 #include "machine.h"
26 
27 #define STACKSIZE 20000
28 
29 typedef Char Stack;
30 
31 #define push(type, value) { \
32     ((type *) (sp += sizeof(type)))[-1] = (value); \
33 }
34 
35 #define pop(type) ( \
36     (*((type *) (sp -= sizeof(type)))) \
37 )
38 
39 #define alignstack() { \
40     sp = (Stack *) (( ((int) sp) + sizeof(int) - 1)&~(sizeof(int) - 1)); \
41 }
42 
43 #endif
44 
45 public Stack stack[STACKSIZE];
46 public Stack *sp = &stack[0];
47 public Boolean useInstLoc = false;
48 
49 #define chksp() \
50 { \
51     if (sp < &stack[0]) { \
52 	panic("stack underflow"); \
53     } \
54 }
55 
56 #define poparg(n, r, fr) { \
57     eval(p->value.arg[n]); \
58     if (isreal(p->op)) { \
59 	fr = pop(double); \
60     } else if (isint(p->op)) { \
61 	r = popsmall(p->value.arg[n]->nodetype); \
62     } \
63 }
64 
65 #define Boolrep char	/* underlying representation type for booleans */
66 
67 /*
68  * Evaluate a parse tree leaving the value on the top of the stack.
69  */
70 
71 public eval(p)
72 register Node p;
73 {
74     long r0, r1;
75     double fr0, fr1;
76     Address addr;
77     long i, n;
78     int len;
79     Symbol s, f;
80     Node n1, n2;
81     Boolean b;
82     File file;
83 
84     checkref(p);
85     if (debug_flag[2]) {
86 	fprintf(stderr," evaluating %s \n",showoperator(p->op));
87     }
88     switch (degree(p->op)) {
89 	case BINARY:
90 	    poparg(1, r1, fr1);
91 	    poparg(0, r0, fr0);
92 	    break;
93 
94 	case UNARY:
95 	    poparg(0, r0, fr0);
96 	    break;
97 
98 	default:
99 	    /* do nothing */;
100     }
101     switch (p->op) {
102 	case O_SYM:
103 	    s = p->value.sym;
104 	    if (s == retaddrsym) {
105 		push(long, return_addr());
106 	    } else {
107 		if (isvariable(s)) {
108 		    if (s != program and not isactive(container(s))) {
109 			error("\"%s\" is not active", symname(s));
110 		    }
111 		    push(long, address(s, nil));
112 		} else if (isblock(s)) {
113 		    push(Symbol, s);
114 		} else {
115 		    error("can't evaluate a %s", classname(s));
116 		}
117 	    }
118 	    break;
119 
120 	case O_LCON:
121 	    r0 = p->value.lcon;
122 	    pushsmall(p->nodetype, r0);
123 	    break;
124 
125 	case O_FCON:
126 	    push(double, p->value.fcon);
127 	    break;
128 
129 	case O_SCON:
130 	    len = size(p->nodetype);
131 	    mov(p->value.scon, sp, len);
132 	    sp += len;
133 	    break;
134 
135 	case O_INDEX:
136 	    n = pop(long);
137 	    i = evalindex(p->value.arg[0]->nodetype,
138 		popsmall(p->value.arg[1]->nodetype));
139 	    push(long, n + i*size(p->nodetype));
140 	    break;
141 
142 	case O_DOT:
143 	    s = p->value.arg[1]->value.sym;
144 	    n = lval(p->value.arg[0]);
145 	    push(long, n + (s->symvalue.field.offset div 8));
146 	    break;
147 
148 	/*
149 	 * Get the value of the expression addressed by the top of the stack.
150 	 * Push the result back on the stack.
151 	 */
152 
153 	case O_INDIR:
154 	case O_RVAL:
155 	    addr = pop(long);
156 	    if (addr == 0) {
157 		error("reference through nil pointer");
158 	    }
159 	    if (p->op == O_INDIR) {
160 		len = sizeof(long);
161 	    } else {
162 		len = size(p->nodetype);
163 	    }
164 	    rpush(addr, len);
165 	addr = pop(long);
166         push(long, addr);
167 	    break;
168 
169 	/*
170 	 * Effectively, we want to pop n bytes off for the evaluated subtree
171 	 * and push len bytes on for the new type of the same tree.
172 	 */
173 	case O_TYPERENAME:
174 	    n = size(p->value.arg[0]->nodetype);
175 	    len = size(p->nodetype);
176 	    sp = sp - n + len;
177 	    break;
178 
179 	case O_COMMA:
180 	    break;
181 
182 	case O_ITOF:
183 	    push(double, (double) r0);
184 	    break;
185 
186 	case O_ADD:
187 	    push(long, r0+r1);
188 	    break;
189 
190 	case O_ADDF:
191 	    push(double, fr0+fr1);
192 	    break;
193 
194 	case O_SUB:
195 	    push(long, r0-r1);
196 	    break;
197 
198 	case O_SUBF:
199 	    push(double, fr0-fr1);
200 	    break;
201 
202 	case O_NEG:
203 	    push(long, -r0);
204 	    break;
205 
206 	case O_NEGF:
207 	    push(double, -fr0);
208 	    break;
209 
210 	case O_MUL:
211 	    push(long, r0*r1);
212 	    break;
213 
214 	case O_MULF:
215 	    push(double, fr0*fr1);
216 	    break;
217 
218 	case O_DIVF:
219 	    if (fr1 == 0) {
220 		error("error: division by 0");
221 	    }
222 	    push(double, fr0 / fr1);
223 	    break;
224 
225 	case O_DIV:
226 	    if (r1 == 0) {
227 		error("error: div by 0");
228 	    }
229 	    push(long, r0 div r1);
230 	    break;
231 
232 	case O_MOD:
233 	    if (r1 == 0) {
234 		error("error: mod by 0");
235 	    }
236 	    push(long, r0 mod r1);
237 	    break;
238 
239 	case O_LT:
240 	    push(Boolrep, r0 < r1);
241 	    break;
242 
243 	case O_LTF:
244 	    push(Boolrep, fr0 < fr1);
245 	    break;
246 
247 	case O_LE:
248 	    push(Boolrep, r0 <= r1);
249 	    break;
250 
251 	case O_LEF:
252 	    push(Boolrep, fr0 <= fr1);
253 	    break;
254 
255 	case O_GT:
256 	    push(Boolrep, r0 > r1);
257 	    break;
258 
259 	case O_GTF:
260 	    push(Boolrep, fr0 > fr1);
261 	    break;
262 
263 	case O_EQ:
264 	    push(Boolrep, r0 == r1);
265 	    break;
266 
267 	case O_EQF:
268 	    push(Boolrep, fr0 == fr1);
269 	    break;
270 
271 	case O_NE:
272 	    push(Boolrep, r0 != r1);
273 	    break;
274 
275 	case O_NEF:
276 	    push(Boolrep, fr0 != fr1);
277 	    break;
278 
279 	case O_AND:
280 	    push(Boolrep, r0 and r1);
281 	    break;
282 
283 	case O_OR:
284 	    push(Boolrep, r0 or r1);
285 	    break;
286 
287 	case O_ASSIGN:
288 	    assign(p->value.arg[0], p->value.arg[1]);
289 	    break;
290 
291 	case O_CHFILE:
292 	    if (p->value.scon == nil) {
293 		printf("%s\n", cursource);
294 	    } else {
295 		file = opensource(p->value.scon);
296 		if (file == nil) {
297 		    error("can't read \"%s\"", p->value.scon);
298 		} else {
299 		    fclose(file);
300 		    setsource(p->value.scon);
301 		}
302 	    }
303 	    break;
304 
305 	case O_CONT:
306 	    cont(p->value.lcon);
307 	    printnews();
308 	    break;
309 
310 	case O_LIST:
311 	    if (p->value.arg[0]->op == O_SYM) {
312 		f = p->value.arg[0]->value.sym;
313 		addr = firstline(f);
314 		if (addr == NOADDR) {
315 		    error("no source lines for \"%s\"", symname(f));
316 		}
317 		setsource(srcfilename(addr));
318 		r0 = srcline(addr) - 5;
319 		r1 = r0 + 10;
320 		if (r0 < 1) {
321 		    r0 = 1;
322 		}
323 	    } else {
324 		eval(p->value.arg[0]);
325 		r0 = pop(long);
326 		eval(p->value.arg[1]);
327 		r1 = pop(long);
328 	    }
329 	    printlines((Lineno) r0, (Lineno) r1);
330 	    break;
331 
332 	case O_FUNC:
333 	    if (p->value.arg[0] == nil) {
334 		printname(stdout, curfunc);
335 		putchar('\n');
336 	    } else {
337 		s = p->value.arg[0]->value.sym;
338 		find(f, s->name) where
339 		    f->class == FUNC or f->class == PROC
340 		endfind(f);
341 		if (f == nil) {
342 		    error("%s is not a procedure or function", symname(s));
343 		}
344 		curfunc = f;
345 		addr = codeloc(curfunc);
346 		if (addr != NOADDR) {
347 		    setsource(srcfilename(addr));
348 		    cursrcline = srcline(addr) - 5;
349 		    if (cursrcline < 1) {
350 			cursrcline = 1;
351 		    }
352 		}
353 	    }
354 	    break;
355 
356 	case O_EXAMINE:
357 	    eval(p->value.examine.beginaddr);
358 	    r0 = pop(long);
359 	    if (p->value.examine.endaddr == nil) {
360 		n = p->value.examine.count;
361 		if (n == 0) {
362 		    printvalue(r0, p->value.examine.mode);
363 		} else if (streq(p->value.examine.mode, "i")) {
364 		    printninst(n, (Address) r0);
365 		} else {
366 		    printndata(n, (Address) r0, p->value.examine.mode);
367 		}
368 	    } else {
369 		eval(p->value.examine.endaddr);
370 		r1 = pop(long);
371 		if (streq(p->value.examine.mode, "i")) {
372 		    printinst((Address)r0, (Address)r1);
373 		} else {
374 		    printdata((Address)r0, (Address)r1, p->value.examine.mode);
375 		}
376 	    }
377 	    break;
378 
379 	case O_PRINT:
380 	    for (n1 = p->value.arg[0]; n1 != nil; n1 = n1->value.arg[1]) {
381 		eval(n1->value.arg[0]);
382 		printval(n1->value.arg[0]->nodetype);
383 		putchar(' ');
384 	    }
385 	    putchar('\n');
386 	    break;
387 
388 	case O_PSYM:
389 	    if (p->value.arg[0]->op == O_SYM) {
390 		psym(p->value.arg[0]->value.sym);
391 	    } else {
392 		psym(p->value.arg[0]->nodetype);
393 	    }
394 	    break;
395 
396 	case O_QLINE:
397 	    eval(p->value.arg[1]);
398 	    break;
399 
400 	case O_STEP:
401 	    b = inst_tracing;
402 	    inst_tracing = (Boolean) (not p->value.step.source);
403 	    if (p->value.step.skipcalls) {
404 		next();
405 	    } else {
406 		stepc();
407 	    }
408 	    inst_tracing = b;
409 	    useInstLoc = (Boolean) (not p->value.step.source);
410 	    printnews();
411 	    break;
412 
413 	case O_WHATIS:
414 	    if (p->value.arg[0]->op == O_SYM) {
415 		printdecl(p->value.arg[0]->value.sym);
416 	    } else {
417 		printdecl(p->value.arg[0]->nodetype);
418 	    }
419 	    break;
420 
421 	case O_WHERE:
422 	    wherecmd();
423 	    break;
424 
425 	case O_WHEREIS:
426 	    if (p->value.arg[0]->op == O_SYM) {
427 		printwhereis(stdout,p->value.arg[0]->value.sym);
428 	    } else {
429 		printwhereis(stdout,p->value.arg[0]->nodetype);
430 	    }
431 	    break;
432 
433 	case O_WHICH:
434 	    if (p->value.arg[0]->op == O_SYM) {
435 		printwhich(stdout,p->value.arg[0]->value.sym);
436 	    } else {
437 		printwhich(stdout,p->value.arg[0]->nodetype);
438 	    }
439 	    putchar('\n');
440 	    break;
441 
442 	case O_ALIAS:
443 	    n1 = p->value.arg[0];
444 	    n2 = p->value.arg[1];
445 	    if (n1 == nil) {
446 		print_alias(nil);
447 	    } else if (n2 == nil) {
448 		print_alias(n1->value.name);
449 	    } else {
450 		enter_alias(n1->value.name, n2->value.name);
451 	    }
452 	    break;
453 
454 	case O_CALL:
455 	    callproc(p->value.arg[0], p->value.arg[1]);
456 	    break;
457 
458 	case O_CATCH:
459 	    psigtrace(process, p->value.lcon, true);
460 	    break;
461 
462 	case O_EDIT:
463 	    edit(p->value.scon);
464 	    break;
465 
466         case O_DEBUG:
467             debug(p);
468 	    break;
469 
470 	case O_DUMP:
471 	    dump();
472 	    break;
473 
474 	case O_GRIPE:
475 	    gripe();
476 	    break;
477 
478 	case O_HELP:
479 	    help();
480 	    break;
481 
482 	case O_IGNORE:
483 	    psigtrace(process, p->value.lcon, false);
484 	    break;
485 
486 	case O_RUN:
487 	    run();
488 	    break;
489 
490 	case O_SOURCE:
491 	    setinput(p->value.scon);
492 	    break;
493 
494 	case O_STATUS:
495 	    status();
496 	    break;
497 
498 	case O_TRACE:
499 	case O_TRACEI:
500 	    trace(p);
501 	    break;
502 
503 	case O_STOP:
504 	case O_STOPI:
505 	    stop(p);
506 	    break;
507 
508 	case O_ADDEVENT:
509 	    addevent(p->value.event.cond, p->value.event.actions);
510 	    break;
511 
512 	case O_DELETE:
513 	    delevent((unsigned int) p->value.lcon);
514 	    break;
515 
516 	case O_ENDX:
517 	    endprogram();
518 	    break;
519 
520 	case O_IF:
521 	    if (cond(p->value.event.cond)) {
522 		evalcmdlist(p->value.event.actions);
523 	    }
524 	    break;
525 
526 	case O_ONCE:
527 	    event_once(p->value.event.cond, p->value.event.actions);
528 	    break;
529 
530 	case O_PRINTCALL:
531 	    printcall(p->value.sym, whatblock(return_addr()));
532 	    break;
533 
534 	case O_PRINTIFCHANGED:
535 	    printifchanged(p->value.arg[0]);
536 	    break;
537 
538 	case O_PRINTRTN:
539 	    printrtn(p->value.sym);
540 	    break;
541 
542 	case O_PRINTSRCPOS:
543 	    getsrcpos();
544 	    if (p->value.arg[0] == nil) {
545 		printsrcpos();
546 		putchar('\n');
547 		printlines(curline, curline);
548 	    } else if (p->value.arg[0]->op == O_QLINE) {
549 		if (p->value.arg[0]->value.arg[1]->value.lcon == 0) {
550 		    printf("tracei: ");
551 		    printinst(pc, pc);
552 		} else {
553 		    printf("trace:  ");
554 		    printlines(curline, curline);
555 		}
556 	    } else {
557 		printsrcpos();
558 		printf(": ");
559 		eval(p->value.arg[0]);
560 		prtree(stdout, p->value.arg[0]);
561 		printf(" = ");
562 		printval(p->value.arg[0]->nodetype);
563 		putchar('\n');
564 	    }
565 	    break;
566 
567 	case O_PROCRTN:
568 	    procreturn(p->value.sym);
569 	    break;
570 
571 	case O_STOPIFCHANGED:
572 	    stopifchanged(p->value.arg[0]);
573 	    break;
574 
575 	case O_STOPX:
576 	    isstopped = true;
577 	    break;
578 
579 	case O_TRACEON:
580 	    traceon(p->value.trace.inst, p->value.trace.event,
581 		p->value.trace.actions);
582 	    break;
583 
584 	case O_TRACEOFF:
585 	    traceoff(p->value.lcon);
586 	    break;
587 
588 	default:
589 	    panic("eval: bad op %d", p->op);
590     }
591  if(debug_flag[2]) {
592 	fprintf(stderr," evaluated %s \n",showoperator(p->op));
593  }
594 
595 }
596 
597 /*
598  * Evaluate a list of commands.
599  */
600 
601 public evalcmdlist(cl)
602 Cmdlist cl;
603 {
604     Command c;
605 
606     foreach (Command, c, cl)
607 	evalcmd(c);
608     endfor
609 }
610 
611 /*
612  * Push "len" bytes onto the expression stack from address "addr"
613  * in the process.  If there isn't room on the stack, print an error message.
614  */
615 
616 public rpush(addr, len)
617 Address addr;
618 int len;
619 {
620     if (not canpush(len)) {
621 	error("expression too large to evaluate");
622     } else {
623 	chksp();
624 	dread(sp, addr, len);
625 	sp += len;
626     }
627 }
628 
629 /*
630  * Check if the stack has n bytes available.
631  */
632 
633 public Boolean canpush(n)
634 Integer n;
635 {
636     return (Boolean) (sp + n < &stack[STACKSIZE]);
637 }
638 
639 /*
640  * Push a small scalar of the given type onto the stack.
641  */
642 
643 public pushsmall(t, v)
644 Symbol t;
645 long v;
646 {
647     register Integer s;
648 
649     s = size(t);
650     switch (s) {
651 	case sizeof(char):
652 	    push(char, v);
653 	    break;
654 
655 	case sizeof(short):
656 	    push(short, v);
657 	    break;
658 
659 	case sizeof(long):
660 	    push(long, v);
661 	    break;
662 
663 	default:
664 	    panic("bad size %d in popsmall", s);
665     }
666 }
667 
668 /*
669  * Pop an item of the given type which is assumed to be no larger
670  * than a long and return it expanded into a long.
671  */
672 
673 public long popsmall(t)
674 Symbol t;
675 {
676     long r;
677 
678     switch (size(t)) {
679 	case sizeof(char):
680 	    r = (long) pop(char);
681 	    break;
682 
683 	case sizeof(short):
684 	    r = (long) pop(short);
685 	    break;
686 
687 	case sizeof(long):
688 	    r = pop(long);
689 	    break;
690 
691 	default:
692 	    panic("popsmall: size is %d", size(t));
693     }
694     return r;
695 }
696 
697 /*
698  * Evaluate a conditional expression.
699  */
700 
701 public Boolean cond(p)
702 Node p;
703 {
704     register Boolean b;
705 
706     if (p == nil) {
707 	b = true;
708     } else {
709 	eval(p);
710 	b = (Boolean) pop(Boolrep);
711     }
712     return b;
713 }
714 
715 /*
716  * Return the address corresponding to a given tree.
717  */
718 
719 public Address lval(p)
720 Node p;
721 {
722     if (p->op == O_RVAL) {
723 	eval(p->value.arg[0]);
724     } else {
725 	eval(p);
726     }
727     return (Address) (pop(long));
728 }
729 
730 /*
731  * Process a trace command, translating into the appropriate events
732  * and associated actions.
733  */
734 
735 public trace(p)
736 Node p;
737 {
738     Node exp, place, cond;
739     Node left;
740 
741     exp = p->value.arg[0];
742     place = p->value.arg[1];
743     cond = p->value.arg[2];
744     if (exp == nil) {
745 	traceall(p->op, place, cond);
746     } else if (exp->op == O_QLINE or exp->op == O_LCON) {
747 	traceinst(p->op, exp, cond);
748     } else if (place != nil and place->op == O_QLINE) {
749 	traceat(p->op, exp, place, cond);
750     } else {
751 	left = exp;
752 	if (left->op == O_RVAL or left->op == O_CALL) {
753 	    left = left->value.arg[0];
754 	}
755 	if (left->op == O_SYM and isblock(left->value.sym)) {
756 	    traceproc(p->op, left->value.sym, place, cond);
757 	} else {
758 	    tracedata(p->op, exp, place, cond);
759 	}
760     }
761 }
762 
763 /*
764  * Set a breakpoint that will turn on tracing.
765  */
766 
767 private traceall(op, place, cond)
768 Operator op;
769 Node place;
770 Node cond;
771 {
772     Symbol s;
773     Node event;
774     Command action;
775 
776     if (place == nil) {
777 	s = program;
778     } else {
779 	s = place->value.sym;
780     }
781     event = build(O_EQ, build(O_SYM, procsym), build(O_SYM, s));
782     action = build(O_PRINTSRCPOS,
783 	build(O_QLINE, nil, build(O_LCON, (op == O_TRACE) ? 1 : 0)));
784     if (cond != nil) {
785 	action = build(O_IF, cond, buildcmdlist(action));
786     }
787     action = build(O_TRACEON, (op == O_TRACEI), buildcmdlist(action));
788     action->value.trace.event = addevent(event, buildcmdlist(action));
789     if (isstdin()) {
790 	printevent(action->value.trace.event);
791     }
792 }
793 
794 /*
795  * Set up the appropriate breakpoint for tracing an instruction.
796  */
797 
798 private traceinst(op, exp, cond)
799 Operator op;
800 Node exp;
801 Node cond;
802 {
803     Node event, wh;
804     Command action;
805     Event e;
806 
807     if (exp->op == O_LCON) {
808 	wh = build(O_QLINE, build(O_SCON, cursource), exp);
809     } else {
810 	wh = exp;
811     }
812     if (op == O_TRACEI) {
813 	event = build(O_EQ, build(O_SYM, pcsym), wh);
814     } else {
815 	event = build(O_EQ, build(O_SYM, linesym), wh);
816     }
817     action = build(O_PRINTSRCPOS, wh);
818     if (cond) {
819 	action = build(O_IF, cond, buildcmdlist(action));
820     }
821     e = addevent(event, buildcmdlist(action));
822     if (isstdin()) {
823 	printevent(e);
824     }
825 }
826 
827 /*
828  * Set a breakpoint to print an expression at a given line or address.
829  */
830 
831 private traceat(op, exp, place, cond)
832 Operator op;
833 Node exp;
834 Node place;
835 Node cond;
836 {
837     Node event;
838     Command action;
839     Event e;
840 
841     if (op == O_TRACEI) {
842 	event = build(O_EQ, build(O_SYM, pcsym), place);
843     } else {
844 	event = build(O_EQ, build(O_SYM, linesym), place);
845     }
846     action = build(O_PRINTSRCPOS, exp);
847     if (cond != nil) {
848 	action = build(O_IF, cond, buildcmdlist(action));
849     }
850     e = addevent(event, buildcmdlist(action));
851     if (isstdin()) {
852 	printevent(e);
853     }
854 }
855 
856 /*
857  * Construct event for tracing a procedure.
858  *
859  * What we want here is
860  *
861  * 	when $proc = p do
862  *	    if <condition> then
863  *	        printcall;
864  *	        once $pc = $retaddr do
865  *	            printrtn;
866  *	        end;
867  *	    end if;
868  *	end;
869  *
870  * Note that "once" is like "when" except that the event
871  * deletes itself as part of its associated action.
872  */
873 
874 private traceproc(op, p, place, cond)
875 Operator op;
876 Symbol p;
877 Node place;
878 Node cond;
879 {
880     Node event;
881     Command action;
882     Cmdlist actionlist;
883     Event e;
884 
885     action = build(O_PRINTCALL, p);
886     actionlist = list_alloc();
887     cmdlist_append(action, actionlist);
888     event = build(O_EQ, build(O_SYM, pcsym), build(O_SYM, retaddrsym));
889     action = build(O_ONCE, event, buildcmdlist(build(O_PRINTRTN, p)));
890     cmdlist_append(action, actionlist);
891     if (cond != nil) {
892 	actionlist = buildcmdlist(build(O_IF, cond, actionlist));
893     }
894     event = build(O_EQ, build(O_SYM, procsym), build(O_SYM, p));
895     e = addevent(event, actionlist);
896     if (isstdin()) {
897 	printevent(e);
898     }
899 }
900 
901 /*
902  * Set up breakpoint for tracing data.
903  */
904 
905 private tracedata(op, exp, place, cond)
906 Operator op;
907 Node exp;
908 Node place;
909 Node cond;
910 {
911     Symbol p;
912     Node event;
913     Command action;
914 
915     p = (place == nil) ? tcontainer(exp) : place->value.sym;
916     if (p == nil) {
917 	p = program;
918     }
919     action = build(O_PRINTIFCHANGED, exp);
920     if (cond != nil) {
921 	action = build(O_IF, cond, buildcmdlist(action));
922     }
923     action = build(O_TRACEON, (op == O_TRACEI), buildcmdlist(action));
924     event = build(O_EQ, build(O_SYM, procsym), build(O_SYM, p));
925     action->value.trace.event = addevent(event, buildcmdlist(action));
926     if (isstdin()) {
927 	printevent(action->value.trace.event);
928     }
929 }
930 
931 /*
932  * Setting and unsetting of stops.
933  */
934 
935 public stop(p)
936 Node p;
937 {
938     Node exp, place, cond, t;
939     Symbol s;
940     Command action;
941     Event e;
942 
943     exp = p->value.arg[0];
944     place = p->value.arg[1];
945     cond = p->value.arg[2];
946     if (exp != nil) {
947 	stopvar(p->op, exp, place, cond);
948     } else {
949 	action = build(O_STOPX);
950 	if (cond != nil) {
951 	    action = build(O_IF, cond, buildcmdlist(action));
952 	}
953 	if (place != nil and place->op == O_SYM) {
954 	    s = place->value.sym;
955 	    t = build(O_EQ, build(O_SYM, procsym), build(O_SYM, s));
956 	    if (cond != nil) {
957 		action = build(O_TRACEON, (p->op == O_STOPI),
958 		    buildcmdlist(action));
959 		e = addevent(t, buildcmdlist(action));
960 		action->value.trace.event = e;
961 	    } else {
962 		e = addevent(t, buildcmdlist(action));
963 	    }
964 	    if (isstdin()) {
965 		printevent(e);
966 	    }
967 	} else {
968 	    stopinst(p->op, place, cond, action);
969 	}
970     }
971 }
972 
973 private stopinst(op, place, cond, action)
974 Operator op;
975 Node place;
976 Node cond;
977 Command action;
978 {
979     Node event;
980     Event e;
981 
982     if (op == O_STOP) {
983 	event = build(O_EQ, build(O_SYM, linesym), place);
984     } else {
985 	event = build(O_EQ, build(O_SYM, pcsym), place);
986     }
987     e = addevent(event, buildcmdlist(action));
988     if (isstdin()) {
989 	printevent(e);
990     }
991 }
992 
993 /*
994  * Implement stopping on assignment to a variable by adding it to
995  * the variable list.
996  */
997 
998 private stopvar(op, exp, place, cond)
999 Operator op;
1000 Node exp;
1001 Node place;
1002 Node cond;
1003 {
1004     Symbol p;
1005     Node event;
1006     Command action;
1007 
1008     if (place == nil) {
1009 	if (exp->op == O_LCON) {
1010 	    p = program;
1011 	} else {
1012 	    p = tcontainer(exp);
1013 	    if (p == nil) {
1014 		p = program;
1015 	    }
1016 	}
1017     } else {
1018 	p = place->value.sym;
1019     }
1020     action = build(O_STOPIFCHANGED, exp);
1021     if (cond != nil) {
1022 	action = build(O_IF, cond, buildcmdlist(action));
1023     }
1024     action = build(O_TRACEON, (op == O_STOPI), buildcmdlist(action));
1025     event = build(O_EQ, build(O_SYM, procsym), build(O_SYM, p));
1026     action->value.trace.event = addevent(event, buildcmdlist(action));
1027     if (isstdin()) {
1028 	printevent(action->value.trace.event);
1029     }
1030 }
1031 
1032 /*
1033  * Assign the value of an expression to a variable (or term).
1034  */
1035 
1036 public assign(var, exp)
1037 Node var;
1038 Node exp;
1039 {
1040     Address addr;
1041     int varsize;
1042     char cvalue;
1043     short svalue;
1044     long lvalue;
1045 
1046     if (not compatible(var->nodetype, exp->nodetype)) {
1047 	error("incompatible types");
1048     }
1049     addr = lval(var);
1050     eval(exp);
1051     varsize = size(var->nodetype);
1052     if (varsize < sizeof(long)) {
1053 	lvalue = pop(long);
1054 	switch (varsize) {
1055 	    case sizeof(char):
1056 		cvalue = lvalue;
1057 		dwrite(&cvalue, addr, varsize);
1058 		break;
1059 
1060 	    case sizeof(short):
1061 		svalue = lvalue;
1062 		dwrite(&svalue, addr, varsize);
1063 		break;
1064 
1065 	    default:
1066 		panic("bad size %d", varsize);
1067 	}
1068     } else {
1069 	sp -= varsize;
1070 	dwrite(sp, addr, varsize);
1071     }
1072 }
1073 
1074 /*
1075  * Send some nasty mail to the current support person.
1076  */
1077 
1078 public gripe()
1079 {
1080     typedef Operation();
1081     Operation *old;
1082     int pid, status;
1083 
1084     char *maintainer = "linton@berkeley";
1085 
1086     puts("Type control-D to end your message.  Be sure to include");
1087     puts("your name and the name of the file you are debugging.");
1088     putchar('\n');
1089     old = signal(SIGINT, SIG_DFL);
1090     pid = back("Mail", stdin, stdout, "-s", "dbx gripe", maintainer, nil);
1091     signal(SIGINT, SIG_IGN);
1092     pwait(pid, &status);
1093     signal(SIGINT, old);
1094     if (status == 0) {
1095 	puts("Thank you.");
1096     } else {
1097 	puts("\nMail not sent.");
1098     }
1099 }
1100 
1101 /*
1102  * Give the user some help.
1103  */
1104 
1105 public help()
1106 {
1107     puts("run                    - begin execution of the program");
1108     puts("cont                   - continue execution");
1109     puts("step                   - single step one line");
1110     puts("next                   - step to next line (skip over calls)");
1111     puts("trace <line#>          - trace execution of the line");
1112     puts("trace <proc>           - trace calls to the procedure");
1113     puts("trace <var>            - trace changes to the variable");
1114     puts("trace <exp> at <line#> - print <exp> when <line> is reached");
1115     puts("stop at <line>         - suspend execution at the line");
1116     puts("stop in <proc>         - suspend execution when <proc> is called");
1117     puts("status                 - print trace/stop's in effect");
1118     puts("delete <number>        - remove trace or stop of given number");
1119     puts("call <proc>            - call the procedure");
1120     puts("where                  - print currently active procedures");
1121     puts("print <exp>            - print the value of the expression");
1122     puts("whatis <name>          - print the declaration of the name");
1123     puts("list <line>, <line>    - list source lines");
1124     puts("edit <proc>            - edit file containing <proc>");
1125     puts("gripe                  - send mail to the person in charge of dbx");
1126     puts("quit                   - exit dbx");
1127 }
1128 
1129 /*
1130  * Divert output to the given file name.
1131  * Cannot redirect to an existing file.
1132  */
1133 
1134 private int so_fd;
1135 private Boolean notstdout;
1136 
1137 public setout(filename)
1138 String filename;
1139 {
1140     File f;
1141 
1142     f = fopen(filename, "r");
1143     if (f != nil) {
1144 	fclose(f);
1145 	error("%s: file already exists", filename);
1146     } else {
1147 	so_fd = dup(1);
1148 	close(1);
1149 	if (creat(filename, 0666) == nil) {
1150 	    unsetout();
1151 	    error("can't create %s", filename);
1152 	}
1153 	notstdout = true;
1154     }
1155 }
1156 
1157 /*
1158  * Revert output to standard output.
1159  */
1160 
1161 public unsetout()
1162 {
1163     fflush(stdout);
1164     close(1);
1165     if (dup(so_fd) != 1) {
1166 	panic("standard out dup failed");
1167     }
1168     close(so_fd);
1169     notstdout = false;
1170 }
1171 
1172 /*
1173  * Determine is standard output is currently being redirected
1174  * to a file (as far as we know).
1175  */
1176 
1177 public Boolean isredirected()
1178 {
1179     return notstdout;
1180 }
1181