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