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