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