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