xref: /csrg-svn/old/dbx/eval.c (revision 11771)
1 /* Copyright (c) 1982 Regents of the University of California */
2 
3 static char sccsid[] = "@(#)eval.c 1.5 03/30/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();
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 		curfunc = p->value.arg[0]->value.sym;
332 		if (isblock(curfunc) and not ismodule(curfunc)) {
333 		    error("%s is not a procedure or function",
334 			symname(curfunc));
335 		}
336 		addr = codeloc(curfunc);
337 		if (addr != NOADDR) {
338 		    setsource(srcfilename(addr));
339 		    cursrcline = srcline(addr) - 5;
340 		    if (cursrcline < 1) {
341 			cursrcline = 1;
342 		    }
343 		}
344 	    }
345 	    break;
346 
347 	case O_EXAMINE:
348 	    eval(p->value.examine.beginaddr);
349 	    r0 = pop(long);
350 	    if (p->value.examine.endaddr == nil) {
351 		n = p->value.examine.count;
352 		if (n == 0) {
353 		    printvalue(r0, p->value.examine.mode);
354 		} else if (streq(p->value.examine.mode, "i")) {
355 		    printninst(n, (Address) r0);
356 		} else {
357 		    printndata(n, (Address) r0, p->value.examine.mode);
358 		}
359 	    } else {
360 		eval(p->value.examine.endaddr);
361 		r1 = pop(long);
362 		if (streq(p->value.examine.mode, "i")) {
363 		    printinst((Address)r0, (Address)r1);
364 		} else {
365 		    printdata((Address)r0, (Address)r1, p->value.examine.mode);
366 		}
367 	    }
368 	    break;
369 
370 	case O_PRINT:
371 	    for (n1 = p->value.arg[0]; n1 != nil; n1 = n1->value.arg[1]) {
372 		eval(n1->value.arg[0]);
373 		printval(n1->value.arg[0]->nodetype);
374 		putchar(' ');
375 	    }
376 	    putchar('\n');
377 	    break;
378 
379 	case O_PSYM:
380 	    if (p->value.arg[0]->op == O_SYM) {
381 		psym(p->value.arg[0]->value.sym);
382 	    } else {
383 		psym(p->value.arg[0]->nodetype);
384 	    }
385 	    break;
386 
387 	case O_QLINE:
388 	    eval(p->value.arg[1]);
389 	    break;
390 
391 	case O_STEP:
392 	    b = inst_tracing;
393 	    inst_tracing = (Boolean) (not p->value.step.source);
394 	    if (p->value.step.skipcalls) {
395 		next();
396 	    } else {
397 		stepc();
398 	    }
399 	    inst_tracing = b;
400 	    printnews();
401 	    break;
402 
403 	case O_WHATIS:
404 	    if (p->value.arg[0]->op == O_SYM) {
405 		printdecl(p->value.arg[0]->value.sym);
406 	    } else {
407 		printdecl(p->value.arg[0]->nodetype);
408 	    }
409 	    break;
410 
411 	case O_WHERE:
412 	    wherecmd();
413 	    break;
414 
415 	case O_WHEREIS:
416 	    printwhereis(stdout, p->value.arg[0]->value.sym);
417 	    break;
418 
419 	case O_WHICH:
420 	    printwhich(stdout, p->value.arg[0]->value.sym);
421 	    putchar('\n');
422 	    break;
423 
424 	case O_ALIAS:
425 	    n1 = p->value.arg[0];
426 	    n2 = p->value.arg[1];
427 	    if (n1 == nil) {
428 		print_alias(nil);
429 	    } else if (n2 == nil) {
430 		print_alias(n1->value.name);
431 	    } else {
432 		enter_alias(n1->value.name, n2->value.name);
433 	    }
434 	    break;
435 
436 	case O_CALL:
437 	    callproc(p->value.arg[0], p->value.arg[1]);
438 	    break;
439 
440 	case O_CATCH:
441 	    psigtrace(process, p->value.lcon, true);
442 	    break;
443 
444 	case O_EDIT:
445 	    edit(p->value.scon);
446 	    break;
447 
448 	case O_DUMP:
449 	    dump();
450 	    break;
451 
452 	case O_GRIPE:
453 	    gripe();
454 	    break;
455 
456 	case O_HELP:
457 	    help();
458 	    break;
459 
460 	case O_IGNORE:
461 	    psigtrace(process, p->value.lcon, false);
462 	    break;
463 
464 	case O_RUN:
465 	    run();
466 	    break;
467 
468 	case O_SOURCE:
469 	    setinput(p->value.scon);
470 	    break;
471 
472 	case O_STATUS:
473 	    status();
474 	    break;
475 
476 	case O_TRACE:
477 	case O_TRACEI:
478 	    trace(p);
479 	    if (isstdin()) {
480 		status();
481 	    }
482 	    break;
483 
484 	case O_STOP:
485 	case O_STOPI:
486 	    stop(p);
487 	    if (isstdin()) {
488 		status();
489 	    }
490 	    break;
491 
492 	case O_ADDEVENT:
493 	    addevent(p->value.event.cond, p->value.event.actions);
494 	    break;
495 
496 	case O_DELETE:
497 	    delevent((unsigned int) p->value.lcon);
498 	    break;
499 
500 	case O_ENDX:
501 	    endprogram();
502 	    break;
503 
504 	case O_IF:
505 	    if (cond(p->value.event.cond)) {
506 		evalcmdlist(p->value.event.actions);
507 	    }
508 	    break;
509 
510 	case O_ONCE:
511 	    event_once(p->value.event.cond, p->value.event.actions);
512 	    break;
513 
514 	case O_PRINTCALL:
515 	    printcall(p->value.sym, whatblock(return_addr()));
516 	    break;
517 
518 	case O_PRINTIFCHANGED:
519 	    printifchanged(p->value.arg[0]);
520 	    break;
521 
522 	case O_PRINTRTN:
523 	    printrtn(p->value.sym);
524 	    break;
525 
526 	case O_PRINTSRCPOS:
527 	    getsrcpos();
528 	    if (p->value.arg[0] == nil) {
529 		printsrcpos();
530 		putchar('\n');
531 		printlines(curline, curline);
532 	    } else if (p->value.arg[0]->op == O_QLINE) {
533 		if (p->value.arg[0]->value.arg[1]->value.lcon == 0) {
534 		    printf("tracei: ");
535 		    printinst(pc, pc);
536 		} else {
537 		    printf("trace:  ");
538 		    printlines(curline, curline);
539 		}
540 	    } else {
541 		printsrcpos();
542 		printf(": ");
543 		eval(p->value.arg[0]);
544 		prtree(stdout, p->value.arg[0]);
545 		printf(" = ");
546 		printval(p->value.arg[0]->nodetype);
547 		putchar('\n');
548 	    }
549 	    break;
550 
551 	case O_PROCRTN:
552 	    procreturn(p->value.sym);
553 	    break;
554 
555 	case O_STOPIFCHANGED:
556 	    stopifchanged(p->value.arg[0]);
557 	    break;
558 
559 	case O_STOPX:
560 	    isstopped = true;
561 	    break;
562 
563 	case O_TRACEON:
564 	    traceon(p->value.trace.inst, p->value.trace.event,
565 		p->value.trace.actions);
566 	    break;
567 
568 	case O_TRACEOFF:
569 	    traceoff(p->value.lcon);
570 	    break;
571 
572 	default:
573 	    panic("eval: bad op %d", p->op);
574     }
575 }
576 
577 /*
578  * Evaluate a list of commands.
579  */
580 
581 public evalcmdlist(cl)
582 Cmdlist cl;
583 {
584     Command c;
585 
586     foreach (Command, c, cl)
587 	evalcmd(c);
588     endfor
589 }
590 
591 /*
592  * Push "len" bytes onto the expression stack from address "addr"
593  * in the process.  If there isn't room on the stack, print an error message.
594  */
595 
596 public rpush(addr, len)
597 Address addr;
598 int len;
599 {
600     if (not canpush(len)) {
601 	error("expression too large to evaluate");
602     } else {
603 	chksp();
604 	dread(sp, addr, len);
605 	sp += len;
606     }
607 }
608 
609 /*
610  * Check if the stack has n bytes available.
611  */
612 
613 public Boolean canpush(n)
614 Integer n;
615 {
616     return (Boolean) (sp + n < &stack[STACKSIZE]);
617 }
618 
619 /*
620  * Push a small scalar of the given type onto the stack.
621  */
622 
623 public pushsmall(t, v)
624 Symbol t;
625 long v;
626 {
627     register Integer s;
628 
629     s = size(t);
630     switch (s) {
631 	case sizeof(char):
632 	    push(char, v);
633 	    break;
634 
635 	case sizeof(short):
636 	    push(short, v);
637 	    break;
638 
639 	case sizeof(long):
640 	    push(long, v);
641 	    break;
642 
643 	default:
644 	    panic("bad size %d in popsmall", s);
645     }
646 }
647 
648 /*
649  * Pop an item of the given type which is assumed to be no larger
650  * than a long and return it expanded into a long.
651  */
652 
653 public long popsmall(t)
654 Symbol t;
655 {
656     long r;
657 
658     switch (size(t)) {
659 	case sizeof(char):
660 	    r = (long) pop(char);
661 	    break;
662 
663 	case sizeof(short):
664 	    r = (long) pop(short);
665 	    break;
666 
667 	case sizeof(long):
668 	    r = pop(long);
669 	    break;
670 
671 	default:
672 	    panic("popsmall: size is %d", size(t));
673     }
674     return r;
675 }
676 
677 /*
678  * Evaluate a conditional expression.
679  */
680 
681 public Boolean cond(p)
682 Node p;
683 {
684     register Boolean b;
685 
686     if (p == nil) {
687 	b = true;
688     } else {
689 	eval(p);
690 	b = pop(Boolean);
691     }
692     return b;
693 }
694 
695 /*
696  * Return the address corresponding to a given tree.
697  */
698 
699 public Address lval(p)
700 Node p;
701 {
702     if (p->op == O_RVAL) {
703 	eval(p->value.arg[0]);
704     } else {
705 	eval(p);
706     }
707     return (Address) (pop(long));
708 }
709 
710 /*
711  * Process a trace command, translating into the appropriate events
712  * and associated actions.
713  */
714 
715 public trace(p)
716 Node p;
717 {
718     Node exp, place, cond;
719     Node left;
720 
721     exp = p->value.arg[0];
722     place = p->value.arg[1];
723     cond = p->value.arg[2];
724     if (exp == nil) {
725 	traceall(p->op, place, cond);
726     } else if (exp->op == O_QLINE or exp->op == O_LCON) {
727 	traceinst(p->op, exp, cond);
728     } else if (place != nil and place->op == O_QLINE) {
729 	traceat(p->op, exp, place, cond);
730     } else {
731 	left = exp;
732 	if (left->op == O_RVAL or left->op == O_CALL) {
733 	    left = left->value.arg[0];
734 	}
735 	if (left->op == O_SYM and isblock(left->value.sym)) {
736 	    traceproc(p->op, left->value.sym, place, cond);
737 	} else {
738 	    tracedata(p->op, exp, place, cond);
739 	}
740     }
741 }
742 
743 /*
744  * Set a breakpoint that will turn on tracing.
745  */
746 
747 private traceall(op, place, cond)
748 Operator op;
749 Node place;
750 Node cond;
751 {
752     Symbol s;
753     Node event;
754     Command action;
755 
756     if (place == nil) {
757 	s = program;
758     } else {
759 	s = place->value.sym;
760     }
761     event = build(O_EQ, build(O_SYM, procsym), build(O_SYM, s));
762     action = build(O_PRINTSRCPOS,
763 	build(O_QLINE, nil, build(O_LCON, (op == O_TRACE) ? 1 : 0)));
764     if (cond != nil) {
765 	action = build(O_IF, cond, buildcmdlist(action));
766     }
767     action = build(O_TRACEON, (op == O_TRACEI), buildcmdlist(action));
768     action->value.trace.event = addevent(event, buildcmdlist(action));
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 
783     if (exp->op == O_LCON) {
784 	wh = build(O_QLINE, build(O_SCON, cursource), exp);
785     } else {
786 	wh = exp;
787     }
788     if (op == O_TRACEI) {
789 	event = build(O_EQ, build(O_SYM, pcsym), wh);
790     } else {
791 	event = build(O_EQ, build(O_SYM, linesym), wh);
792     }
793     action = build(O_PRINTSRCPOS, wh);
794     if (cond) {
795 	action = build(O_IF, cond, buildcmdlist(action));
796     }
797     addevent(event, buildcmdlist(action));
798 }
799 
800 /*
801  * Set a breakpoint to print an expression at a given line or address.
802  */
803 
804 private traceat(op, exp, place, cond)
805 Operator op;
806 Node exp;
807 Node place;
808 Node cond;
809 {
810     Node event;
811     Command action;
812 
813     if (op == O_TRACEI) {
814 	event = build(O_EQ, build(O_SYM, pcsym), place);
815     } else {
816 	event = build(O_EQ, build(O_SYM, linesym), place);
817     }
818     action = build(O_PRINTSRCPOS, exp);
819     if (cond != nil) {
820 	action = build(O_IF, cond, buildcmdlist(action));
821     }
822     addevent(event, buildcmdlist(action));
823 }
824 
825 /*
826  * Construct event for tracing a procedure.
827  *
828  * What we want here is
829  *
830  * 	when $proc = p do
831  *	    if <condition> then
832  *	        printcall;
833  *	        once $pc = $retaddr do
834  *	            printrtn;
835  *	        end;
836  *	    end if;
837  *	end;
838  *
839  * Note that "once" is like "when" except that the event
840  * deletes itself as part of its associated action.
841  */
842 
843 private traceproc(op, p, place, cond)
844 Operator op;
845 Symbol p;
846 Node place;
847 Node cond;
848 {
849     Node event;
850     Command action;
851     Cmdlist actionlist;
852 
853     action = build(O_PRINTCALL, p);
854     actionlist = list_alloc();
855     cmdlist_append(action, actionlist);
856     event = build(O_EQ, build(O_SYM, pcsym), build(O_SYM, retaddrsym));
857     action = build(O_ONCE, event, buildcmdlist(build(O_PRINTRTN, p)));
858     cmdlist_append(action, actionlist);
859     if (cond != nil) {
860 	actionlist = buildcmdlist(build(O_IF, cond, actionlist));
861     }
862     event = build(O_EQ, build(O_SYM, procsym), build(O_SYM, p));
863     addevent(event, actionlist);
864 }
865 
866 /*
867  * Set up breakpoint for tracing data.
868  */
869 
870 private tracedata(op, exp, place, cond)
871 Operator op;
872 Node exp;
873 Node place;
874 Node cond;
875 {
876     Symbol p;
877     Node event;
878     Command action;
879 
880     p = (place == nil) ? tcontainer(exp) : place->value.sym;
881     if (p == nil) {
882 	p = program;
883     }
884     action = build(O_PRINTIFCHANGED, exp);
885     if (cond != nil) {
886 	action = build(O_IF, cond, buildcmdlist(action));
887     }
888     action = build(O_TRACEON, (op == O_TRACEI), buildcmdlist(action));
889     event = build(O_EQ, build(O_SYM, procsym), build(O_SYM, p));
890     action->value.trace.event = addevent(event, buildcmdlist(action));
891 }
892 
893 /*
894  * Setting and unsetting of stops.
895  */
896 
897 public stop(p)
898 Node p;
899 {
900     Node exp, place, cond;
901     Symbol s;
902     Command action;
903 
904     exp = p->value.arg[0];
905     place = p->value.arg[1];
906     cond = p->value.arg[2];
907     if (exp != nil) {
908 	stopvar(p->op, exp, place, cond);
909     } else if (cond != nil) {
910 	s = (place == nil) ? program : place->value.sym;
911 	action = build(O_IF, cond, buildcmdlist(build(O_STOPX)));
912 	action = build(O_TRACEON, (p->op == O_STOPI), buildcmdlist(action));
913 	cond = build(O_EQ, build(O_SYM, procsym), build(O_SYM, s));
914 	action->value.trace.event = addevent(cond, buildcmdlist(action));
915     } else if (place->op == O_SYM) {
916 	s = place->value.sym;
917 	cond = build(O_EQ, build(O_SYM, procsym), build(O_SYM, s));
918 	addevent(cond, buildcmdlist(build(O_STOPX)));
919     } else {
920 	stopinst(p->op, place, cond);
921     }
922 }
923 
924 private stopinst(op, place, cond)
925 Operator op;
926 Node place;
927 Node cond;
928 {
929     Node event;
930 
931     if (op == O_STOP) {
932 	event = build(O_EQ, build(O_SYM, linesym), place);
933     } else {
934 	event = build(O_EQ, build(O_SYM, pcsym), place);
935     }
936     addevent(event, buildcmdlist(build(O_STOPX)));
937 }
938 
939 /*
940  * Implement stopping on assignment to a variable by adding it to
941  * the variable list.
942  */
943 
944 private stopvar(op, exp, place, cond)
945 Operator op;
946 Node exp;
947 Node place;
948 Node cond;
949 {
950     Symbol p;
951     Node event;
952     Command action;
953 
954     p = (place == nil) ? tcontainer(exp) : place->value.sym;
955     if (p == nil) {
956 	p = program;
957     }
958     action = build(O_IF, cond, buildcmdlist(build(O_STOPIFCHANGED, exp)));
959     action = build(O_TRACEON, (op == O_STOPI), buildcmdlist(action));
960     event = build(O_EQ, build(O_SYM, procsym), build(O_SYM, p));
961     action->value.trace.event = addevent(event, buildcmdlist(action));
962 }
963 
964 /*
965  * Assign the value of an expression to a variable (or term).
966  */
967 
968 public assign(var, exp)
969 Node var;
970 Node exp;
971 {
972     Address addr;
973     int varsize;
974     char cvalue;
975     short svalue;
976     long lvalue;
977 
978     if (not compatible(var->nodetype, exp->nodetype)) {
979 	error("incompatible types");
980     }
981     addr = lval(var);
982     eval(exp);
983     varsize = size(var->nodetype);
984     if (varsize < sizeof(long)) {
985 	lvalue = pop(long);
986 	switch (varsize) {
987 	    case sizeof(char):
988 		cvalue = lvalue;
989 		dwrite(&cvalue, addr, varsize);
990 		break;
991 
992 	    case sizeof(short):
993 		svalue = lvalue;
994 		dwrite(&svalue, addr, varsize);
995 		break;
996 
997 	    default:
998 		panic("bad size %d", varsize);
999 	}
1000     } else {
1001 	sp -= varsize;
1002 	dwrite(sp, addr, varsize);
1003     }
1004 }
1005 
1006 #define DEF_EDITOR  "vi"
1007 
1008 /*
1009  * Invoke an editor on the given file.  Which editor to use might change
1010  * installation to installation.  For now, we use "vi".  In any event,
1011  * the environment variable "EDITOR" overrides any default.
1012  */
1013 
1014 public edit(filename)
1015 String filename;
1016 {
1017     extern String getenv();
1018     String ed, src;
1019     File f;
1020     Symbol s;
1021     Address addr;
1022     char buff[10];
1023 
1024     ed = getenv("EDITOR");
1025     if (ed == nil) {
1026 	ed = DEF_EDITOR;
1027     }
1028     if (filename == nil) {
1029 	call(ed, stdin, stdout, cursource, nil);
1030     } else {
1031 	f = fopen(filename, "r");
1032 	if (f == nil) {
1033 	    s = which(identname(filename, true));
1034 	    if (not isblock(s)) {
1035 		error("can't read \"%s\"", filename);
1036 	    }
1037 	    addr = firstline(s);
1038 	    if (addr == NOADDR) {
1039 		error("no source for \"%s\"", filename);
1040 	    }
1041 	    src = srcfilename(addr);
1042 	    sprintf(buff, "+%d", srcline(addr));
1043 	    call(ed, stdin, stdout, buff, src, nil);
1044 	} else {
1045 	    fclose(f);
1046 	    call(ed, stdin, stdout, filename, nil);
1047 	}
1048     }
1049 }
1050 
1051 /*
1052  * Send some nasty mail to the current support person.
1053  */
1054 
1055 public gripe()
1056 {
1057     typedef Operation();
1058     Operation *old;
1059 
1060     char *maintainer = "linton@ucbarpa";
1061 
1062     puts("Type control-D to end your message.  Be sure to include");
1063     puts("your name and the name of the file you are debugging.");
1064     putchar('\n');
1065     old = signal(SIGINT, SIG_DFL);
1066     call("Mail", stdin, stdout, maintainer, nil);
1067     signal(SIGINT, old);
1068     puts("Thank you.");
1069 }
1070 
1071 /*
1072  * Give the user some help.
1073  */
1074 
1075 public help()
1076 {
1077     puts("run                    - begin execution of the program");
1078     puts("cont                   - continue execution");
1079     puts("step                   - single step one line");
1080     puts("next                   - step to next line (skip over calls)");
1081     puts("trace <line#>          - trace execution of the line");
1082     puts("trace <proc>           - trace calls to the procedure");
1083     puts("trace <var>            - trace changes to the variable");
1084     puts("trace <exp> at <line#> - print <exp> when <line> is reached");
1085     puts("stop at <line>         - suspend execution at the line");
1086     puts("stop in <proc>         - suspend execution when <proc> is called");
1087     puts("status                 - print trace/stop's in effect");
1088     puts("delete <number>        - remove trace or stop of given number");
1089     puts("call <proc>            - call the procedure");
1090     puts("where                  - print currently active procedures");
1091     puts("print <exp>            - print the value of the expression");
1092     puts("whatis <name>          - print the declaration of the name");
1093     puts("list <line>, <line>    - list source lines");
1094     puts("edit <proc>            - edit file containing <proc>");
1095     puts("gripe                  - send mail to the person in charge of dbx");
1096     puts("quit                   - exit dbx");
1097 }
1098 
1099 /*
1100  * Divert output to the given file name.
1101  * Cannot redirect to an existing file.
1102  */
1103 
1104 private int so_fd;
1105 private Boolean notstdout;
1106 
1107 public setout(filename)
1108 String filename;
1109 {
1110     File f;
1111 
1112     f = fopen(filename, "r");
1113     if (f != nil) {
1114 	fclose(f);
1115 	error("%s: file already exists", filename);
1116     } else {
1117 	so_fd = dup(1);
1118 	close(1);
1119 	if (creat(filename, 0666) == nil) {
1120 	    unsetout();
1121 	    error("can't create %s", filename);
1122 	}
1123 	notstdout = true;
1124     }
1125 }
1126 
1127 /*
1128  * Revert output to standard output.
1129  */
1130 
1131 public unsetout()
1132 {
1133     fflush(stdout);
1134     close(1);
1135     if (dup(so_fd) != 1) {
1136 	panic("standard out dup failed");
1137     }
1138     close(so_fd);
1139     notstdout = false;
1140 }
1141 
1142 /*
1143  * Determine is standard output is currently being redirected
1144  * to a file (as far as we know).
1145  */
1146 
1147 public Boolean isredirected()
1148 {
1149     return notstdout;
1150 }
1151