xref: /csrg-svn/old/dbx/process.c (revision 16617)
19677Slinton /* Copyright (c) 1982 Regents of the University of California */
29677Slinton 
3*16617Ssam static char sccsid[] = "@(#)process.c 1.12 8/19/83";
49677Slinton 
5*16617Ssam static char rcsid[] = "$Header: process.c,v 1.3 84/03/27 10:23:24 linton Exp $";
6*16617Ssam 
79677Slinton /*
89677Slinton  * Process management.
99677Slinton  *
109677Slinton  * This module contains the routines to manage the execution and
119677Slinton  * tracing of the debuggee process.
129677Slinton  */
139677Slinton 
149677Slinton #include "defs.h"
159677Slinton #include "process.h"
169677Slinton #include "machine.h"
179677Slinton #include "events.h"
189677Slinton #include "tree.h"
1914757Slinton #include "eval.h"
209677Slinton #include "operators.h"
219677Slinton #include "source.h"
229677Slinton #include "object.h"
239677Slinton #include "mappings.h"
249677Slinton #include "main.h"
259677Slinton #include "coredump.h"
269677Slinton #include <signal.h>
279677Slinton #include <errno.h>
289677Slinton #include <sys/param.h>
29*16617Ssam #include <sys/dir.h>
30*16617Ssam #include <sys/user.h>
319843Slinton #include <machine/reg.h>
329677Slinton #include <sys/stat.h>
339677Slinton 
349677Slinton #ifndef public
359677Slinton 
369677Slinton typedef struct Process *Process;
379677Slinton 
389677Slinton Process process;
399677Slinton 
4014757Slinton #define DEFSIG -1
4114757Slinton 
429677Slinton #include "machine.h"
439677Slinton 
449677Slinton #endif
459677Slinton 
469677Slinton #define NOTSTARTED 1
479677Slinton #define STOPPED 0177
489677Slinton #define FINISHED 0
499677Slinton 
509677Slinton /*
51*16617Ssam  * A cache of the instruction segment is kept to reduce the number
52*16617Ssam  * of system calls.  Might be better just to read the entire
53*16617Ssam  * code space into memory.
549677Slinton  */
559677Slinton 
569677Slinton #define CSIZE 1003       /* size of instruction cache */
579677Slinton 
589677Slinton typedef struct {
599677Slinton     Word addr;
609677Slinton     Word val;
619677Slinton } CacheWord;
629677Slinton 
639677Slinton /*
649677Slinton  * This structure holds the information we need from the user structure.
659677Slinton  */
669677Slinton 
679677Slinton struct Process {
689677Slinton     int pid;			/* process being traced */
6911768Slinton     int mask;			/* process status word */
7011768Slinton     Word reg[NREG];		/* process' registers */
719677Slinton     Word oreg[NREG];		/* registers when process last stopped */
729677Slinton     short status;		/* either STOPPED or FINISHED */
739677Slinton     short signo;		/* signal that stopped process */
749677Slinton     int exitval;		/* return value from exit() */
759677Slinton     long sigset;		/* bit array of traced signals */
769677Slinton     CacheWord word[CSIZE];	/* text segment cache */
7711768Slinton     Ttyinfo ttyinfo;		/* process' terminal characteristics */
78*16617Ssam     Address sigstatus;		/* process' handler for current signal */
799677Slinton };
809677Slinton 
819677Slinton /*
829677Slinton  * These definitions are for the arguments to "pio".
839677Slinton  */
849677Slinton 
859677Slinton typedef enum { PREAD, PWRITE } PioOp;
869677Slinton typedef enum { TEXTSEG, DATASEG } PioSeg;
879677Slinton 
889677Slinton private struct Process pbuf;
899677Slinton 
9013841Slinton #define MAXNCMDARGS 100         /* maximum number of arguments to RUN */
919677Slinton 
9214395Slinton extern int errno;
9314395Slinton 
949677Slinton private Boolean just_started;
959677Slinton private int argc;
969677Slinton private String argv[MAXNCMDARGS];
979677Slinton private String infile, outfile;
989677Slinton 
999677Slinton /*
1009677Slinton  * Initialize process information.
1019677Slinton  */
1029677Slinton 
1039677Slinton public process_init()
1049677Slinton {
1059677Slinton     register Integer i;
1069677Slinton     Char buf[10];
1079677Slinton 
1089677Slinton     process = &pbuf;
1099677Slinton     process->status = (coredump) ? STOPPED : NOTSTARTED;
1109677Slinton     setsigtrace();
1119677Slinton     for (i = 0; i < NREG; i++) {
1129677Slinton 	sprintf(buf, "$r%d", i);
1139677Slinton 	defregname(identname(buf, false), i);
1149677Slinton     }
1159677Slinton     defregname(identname("$ap", true), ARGP);
1169677Slinton     defregname(identname("$fp", true), FRP);
1179677Slinton     defregname(identname("$sp", true), STKP);
1189677Slinton     defregname(identname("$pc", true), PROGCTR);
1199677Slinton     if (coredump) {
1209677Slinton 	coredump_readin(process->mask, process->reg, process->signo);
12112484Slinton 	pc = process->reg[PROGCTR];
12212484Slinton 	getsrcpos();
1239677Slinton     }
12412484Slinton     arginit();
1259677Slinton }
1269677Slinton 
1279677Slinton /*
1289677Slinton  * Routines to get at process information from outside this module.
1299677Slinton  */
1309677Slinton 
1319677Slinton public Word reg(n)
1329677Slinton Integer n;
1339677Slinton {
1349677Slinton     register Word w;
1359677Slinton 
1369677Slinton     if (n == NREG) {
1379677Slinton 	w = process->mask;
1389677Slinton     } else {
1399677Slinton 	w = process->reg[n];
1409677Slinton     }
1419677Slinton     return w;
1429677Slinton }
1439677Slinton 
1449677Slinton public setreg(n, w)
1459677Slinton Integer n;
1469677Slinton Word w;
1479677Slinton {
1489677Slinton     process->reg[n] = w;
1499677Slinton }
1509677Slinton 
1519677Slinton /*
1529677Slinton  * Begin execution.
1539677Slinton  *
1549677Slinton  * We set a breakpoint at the end of the code so that the
1559677Slinton  * process data doesn't disappear after the program terminates.
1569677Slinton  */
1579677Slinton 
1589677Slinton private Boolean remade();
1599677Slinton 
1609677Slinton public start(argv, infile, outfile)
1619677Slinton String argv[];
1629677Slinton String infile, outfile;
1639677Slinton {
1649677Slinton     String pargv[4];
1659677Slinton     Node cond;
1669677Slinton 
1679677Slinton     if (coredump) {
1689677Slinton 	coredump = false;
1699677Slinton 	fclose(corefile);
1709677Slinton 	coredump_close();
1719677Slinton     }
1729677Slinton     if (argv == nil) {
1739677Slinton 	argv = pargv;
1749677Slinton 	pargv[0] = objname;
1759677Slinton 	pargv[1] = nil;
1769677Slinton     } else {
1779677Slinton 	argv[argc] = nil;
1789677Slinton     }
1799677Slinton     if (remade(objname)) {
1809677Slinton 	reinit(argv, infile, outfile);
1819677Slinton     }
1829677Slinton     pstart(process, argv, infile, outfile);
1839677Slinton     if (process->status == STOPPED) {
1849677Slinton 	pc = 0;
185*16617Ssam 	setcurfunc(program);
1869677Slinton 	if (objsize != 0) {
1879677Slinton 	    cond = build(O_EQ, build(O_SYM, pcsym), build(O_LCON, lastaddr()));
1889677Slinton 	    event_once(cond, buildcmdlist(build(O_ENDX)));
1899677Slinton 	}
1909677Slinton     }
1919677Slinton }
1929677Slinton 
1939677Slinton /*
1949677Slinton  * Check to see if the object file has changed since the symbolic
1959677Slinton  * information last was read.
1969677Slinton  */
1979677Slinton 
1989677Slinton private time_t modtime;
1999677Slinton 
2009677Slinton private Boolean remade(filename)
2019677Slinton String filename;
2029677Slinton {
2039677Slinton     struct stat s;
2049677Slinton     Boolean b;
2059677Slinton 
2069677Slinton     stat(filename, &s);
2079677Slinton     b = (Boolean) (modtime != 0 and modtime < s.st_mtime);
2089677Slinton     modtime = s.st_mtime;
2099677Slinton     return b;
2109677Slinton }
2119677Slinton 
2129677Slinton /*
2139677Slinton  * Set up what signals we want to trace.
2149677Slinton  */
2159677Slinton 
2169677Slinton private setsigtrace()
2179677Slinton {
2189677Slinton     register Integer i;
2199677Slinton     register Process p;
2209677Slinton 
2219677Slinton     p = process;
2229677Slinton     for (i = 1; i <= NSIG; i++) {
2239677Slinton 	psigtrace(p, i, true);
2249677Slinton     }
2259677Slinton     psigtrace(p, SIGHUP, false);
2269677Slinton     psigtrace(p, SIGKILL, false);
2279677Slinton     psigtrace(p, SIGALRM, false);
2289677Slinton     psigtrace(p, SIGTSTP, false);
2299677Slinton     psigtrace(p, SIGCONT, false);
2309677Slinton     psigtrace(p, SIGCHLD, false);
2319677Slinton }
2329677Slinton 
2339677Slinton /*
2349677Slinton  * Initialize the argument list.
2359677Slinton  */
2369677Slinton 
2379677Slinton public arginit()
2389677Slinton {
2399677Slinton     infile = nil;
2409677Slinton     outfile = nil;
2419677Slinton     argv[0] = objname;
2429677Slinton     argc = 1;
2439677Slinton }
2449677Slinton 
2459677Slinton /*
2469677Slinton  * Add an argument to the list for the debuggee.
2479677Slinton  */
2489677Slinton 
2499677Slinton public newarg(arg)
2509677Slinton String arg;
2519677Slinton {
2529677Slinton     if (argc >= MAXNCMDARGS) {
2539677Slinton 	error("too many arguments");
2549677Slinton     }
2559677Slinton     argv[argc++] = arg;
2569677Slinton }
2579677Slinton 
2589677Slinton /*
2599677Slinton  * Set the standard input for the debuggee.
2609677Slinton  */
2619677Slinton 
2629677Slinton public inarg(filename)
2639677Slinton String filename;
2649677Slinton {
2659677Slinton     if (infile != nil) {
2669677Slinton 	error("multiple input redirects");
2679677Slinton     }
2689677Slinton     infile = filename;
2699677Slinton }
2709677Slinton 
2719677Slinton /*
2729677Slinton  * Set the standard output for the debuggee.
2739677Slinton  * Probably should check to avoid overwriting an existing file.
2749677Slinton  */
2759677Slinton 
2769677Slinton public outarg(filename)
2779677Slinton String filename;
2789677Slinton {
2799677Slinton     if (outfile != nil) {
2809677Slinton 	error("multiple output redirect");
2819677Slinton     }
2829677Slinton     outfile = filename;
2839677Slinton }
2849677Slinton 
2859677Slinton /*
2869677Slinton  * Start debuggee executing.
2879677Slinton  */
2889677Slinton 
2899677Slinton public run()
2909677Slinton {
2919677Slinton     process->status = STOPPED;
2929677Slinton     fixbps();
2939677Slinton     curline = 0;
2949677Slinton     start(argv, infile, outfile);
2959677Slinton     just_started = true;
2969677Slinton     isstopped = false;
29714757Slinton     cont(0);
2989677Slinton }
2999677Slinton 
3009677Slinton /*
3019677Slinton  * Continue execution wherever we left off.
3029677Slinton  *
3039677Slinton  * Note that this routine never returns.  Eventually bpact() will fail
3049677Slinton  * and we'll call printstatus or step will call it.
3059677Slinton  */
3069677Slinton 
3079677Slinton typedef int Intfunc();
3089677Slinton 
3099677Slinton private Intfunc *dbintr;
3109677Slinton private intr();
3119677Slinton 
3129677Slinton #define succeeds    == true
3139677Slinton #define fails       == false
3149677Slinton 
31511867Slinton public cont(signo)
316*16617Ssam integer signo;
3179677Slinton {
318*16617Ssam     integer s;
319*16617Ssam 
3209677Slinton     dbintr = signal(SIGINT, intr);
3219677Slinton     if (just_started) {
3229677Slinton 	just_started = false;
3239677Slinton     } else {
3249677Slinton 	if (not isstopped) {
3259677Slinton 	    error("can't continue execution");
3269677Slinton 	}
3279677Slinton 	isstopped = false;
32811867Slinton 	stepover();
3299677Slinton     }
330*16617Ssam     s = signo;
3319677Slinton     for (;;) {
3329677Slinton 	if (single_stepping) {
3339677Slinton 	    printnews();
3349677Slinton 	} else {
3359677Slinton 	    setallbps();
336*16617Ssam 	    resume(s);
3379677Slinton 	    unsetallbps();
338*16617Ssam 	    s = DEFSIG;
3399677Slinton 	    if (bpact() fails) {
3409677Slinton 		printstatus();
3419677Slinton 	    }
3429677Slinton 	}
34311867Slinton 	stepover();
3449677Slinton     }
3459677Slinton     /* NOTREACHED */
3469677Slinton }
3479677Slinton 
3489677Slinton /*
3499677Slinton  * This routine is called if we get an interrupt while "running" px
3509677Slinton  * but actually in the debugger.  Could happen, for example, while
3519677Slinton  * processing breakpoints.
3529677Slinton  *
3539677Slinton  * We basically just want to keep going; the assumption is
3549677Slinton  * that when the process resumes it will get the interrupt
3559677Slinton  * which will then be handled.
3569677Slinton  */
3579677Slinton 
3589677Slinton private intr()
3599677Slinton {
3609677Slinton     signal(SIGINT, intr);
3619677Slinton }
3629677Slinton 
3639677Slinton public fixintr()
3649677Slinton {
3659677Slinton     signal(SIGINT, dbintr);
3669677Slinton }
3679677Slinton 
3689677Slinton /*
3699677Slinton  * Resume execution.
3709677Slinton  */
3719677Slinton 
37211867Slinton public resume(signo)
37311867Slinton int signo;
3749677Slinton {
3759677Slinton     register Process p;
3769677Slinton 
3779677Slinton     p = process;
37811867Slinton     pcont(p, signo);
3799677Slinton     pc = process->reg[PROGCTR];
38011832Slinton     if (p->status != STOPPED) {
38111867Slinton 	if (p->signo != 0) {
38211867Slinton 	    error("program terminated by signal %d", p->signo);
38314757Slinton 	} else if (not runfirst) {
38411867Slinton 	    error("program unexpectedly exited with %d", p->exitval);
38511867Slinton 	}
38611832Slinton     }
3879677Slinton }
3889677Slinton 
3899677Slinton /*
3909677Slinton  * Continue execution up to the next source line.
3919677Slinton  *
3929677Slinton  * There are two ways to define the next source line depending on what
3939677Slinton  * is desired when a procedure or function call is encountered.  Step
3949677Slinton  * stops at the beginning of the procedure or call; next skips over it.
3959677Slinton  */
3969677Slinton 
3979677Slinton /*
3989677Slinton  * Stepc is what is called when the step command is given.
3999677Slinton  * It has to play with the "isstopped" information.
4009677Slinton  */
4019677Slinton 
4029677Slinton public stepc()
4039677Slinton {
4049677Slinton     if (not isstopped) {
4059677Slinton 	error("can't continue execution");
4069677Slinton     }
4079677Slinton     isstopped = false;
4089677Slinton     dostep(false);
4099677Slinton     isstopped = true;
4109677Slinton }
4119677Slinton 
4129677Slinton public next()
4139677Slinton {
414*16617Ssam     Address oldfrp, newfrp;
415*16617Ssam 
4169677Slinton     if (not isstopped) {
4179677Slinton 	error("can't continue execution");
4189677Slinton     }
4199677Slinton     isstopped = false;
420*16617Ssam     oldfrp = reg(FRP);
421*16617Ssam     do {
422*16617Ssam 	dostep(true);
423*16617Ssam 	pc = reg(PROGCTR);
424*16617Ssam 	newfrp = reg(FRP);
425*16617Ssam     } while (newfrp < oldfrp and newfrp != 0);
4269677Slinton     isstopped = true;
4279677Slinton }
4289677Slinton 
42911867Slinton /*
430*16617Ssam  * Continue execution until the current function returns, or,
431*16617Ssam  * if the given argument is non-nil, until execution returns to
432*16617Ssam  * somewhere within the given function.
433*16617Ssam  */
434*16617Ssam 
435*16617Ssam public rtnfunc (f)
436*16617Ssam Symbol f;
437*16617Ssam {
438*16617Ssam     Address addr;
439*16617Ssam     Symbol t;
440*16617Ssam 
441*16617Ssam     if (not isstopped) {
442*16617Ssam 	error("can't continue execution");
443*16617Ssam     } else if (f != nil and not isactive(f)) {
444*16617Ssam 	error("%s is not active", symname(f));
445*16617Ssam     } else {
446*16617Ssam 	addr = return_addr();
447*16617Ssam 	if (addr == nil) {
448*16617Ssam 	    error("no place to return to");
449*16617Ssam 	} else {
450*16617Ssam 	    isstopped = false;
451*16617Ssam 	    contto(addr);
452*16617Ssam 	    if (f != nil) {
453*16617Ssam 		for (;;) {
454*16617Ssam 		    t = whatblock(pc);
455*16617Ssam 		    addr = return_addr();
456*16617Ssam 		if (t == f or addr == nil) break;
457*16617Ssam 		    contto(addr);
458*16617Ssam 		}
459*16617Ssam 	    }
460*16617Ssam 	    if (bpact() fails) {
461*16617Ssam 		isstopped = true;
462*16617Ssam 		printstatus();
463*16617Ssam 	    }
464*16617Ssam 	}
465*16617Ssam     }
466*16617Ssam }
467*16617Ssam 
468*16617Ssam /*
46911867Slinton  * Single-step over the current machine instruction.
47011867Slinton  *
47111867Slinton  * If we're single-stepping by source line we want to step to the
47211867Slinton  * next source line.  Otherwise we're going to continue so there's
47311867Slinton  * no reason to do all the work necessary to single-step to the next
47411867Slinton  * source line.
47511867Slinton  */
47611867Slinton 
477*16617Ssam public stepover()
4789677Slinton {
47911867Slinton     Boolean b;
48011867Slinton 
481*16617Ssam     if (traceexec) {
482*16617Ssam 	printf("!! stepping over 0x%x\n", process->reg[PROGCTR]);
483*16617Ssam     }
48411867Slinton     if (single_stepping) {
48511867Slinton 	dostep(false);
48611867Slinton     } else {
48711867Slinton 	b = inst_tracing;
48811867Slinton 	inst_tracing = true;
48911867Slinton 	dostep(false);
49011867Slinton 	inst_tracing = b;
49111867Slinton     }
492*16617Ssam     if (traceexec) {
493*16617Ssam 	printf("!! stepped over to 0x%x\n", process->reg[PROGCTR]);
494*16617Ssam     }
4959677Slinton }
4969677Slinton 
4979677Slinton /*
4989677Slinton  * Resume execution up to the given address.  It is assumed that
4999677Slinton  * no breakpoints exist between the current address and the one
5009677Slinton  * we're stepping to.  This saves us from setting all the breakpoints.
5019677Slinton  */
5029677Slinton 
5039677Slinton public stepto(addr)
5049677Slinton Address addr;
5059677Slinton {
506*16617Ssam     xto(addr, false);
507*16617Ssam }
508*16617Ssam 
509*16617Ssam private contto (addr)
510*16617Ssam Address addr;
511*16617Ssam {
512*16617Ssam     xto(addr, true);
513*16617Ssam }
514*16617Ssam 
515*16617Ssam private xto (addr, catchbps)
516*16617Ssam Address addr;
517*16617Ssam boolean catchbps;
518*16617Ssam {
519*16617Ssam     Address curpc;
520*16617Ssam 
521*16617Ssam     if (catchbps) {
522*16617Ssam 	stepover();
5239677Slinton     }
524*16617Ssam     curpc = process->reg[PROGCTR];
525*16617Ssam     if (addr != curpc) {
526*16617Ssam 	if (traceexec) {
527*16617Ssam 	    printf("!! stepping from 0x%x to 0x%x\n", curpc, addr);
528*16617Ssam 	}
529*16617Ssam 	if (catchbps) {
530*16617Ssam 	    setallbps();
531*16617Ssam 	}
532*16617Ssam 	setbp(addr);
533*16617Ssam 	resume(DEFSIG);
534*16617Ssam 	unsetbp(addr);
535*16617Ssam 	if (catchbps) {
536*16617Ssam 	    unsetallbps();
537*16617Ssam 	}
538*16617Ssam 	if (not isbperr()) {
539*16617Ssam 	    printstatus();
540*16617Ssam 	}
541*16617Ssam     }
5429677Slinton }
5439677Slinton 
5449677Slinton /*
5459677Slinton  * Print the status of the process.
5469677Slinton  * This routine does not return.
5479677Slinton  */
5489677Slinton 
5499677Slinton public printstatus()
5509677Slinton {
55114395Slinton     int status;
55214395Slinton 
5539843Slinton     if (process->status == FINISHED) {
5549843Slinton 	exit(0);
5559843Slinton     } else {
556*16617Ssam 	setcurfunc(whatblock(pc));
5579677Slinton 	getsrcpos();
5589843Slinton 	if (process->signo == SIGINT) {
5599843Slinton 	    isstopped = true;
5609843Slinton 	    printerror();
5619843Slinton 	} else if (isbperr() and isstopped) {
5629843Slinton 	    printf("stopped ");
56311172Slinton 	    printloc();
56411172Slinton 	    putchar('\n');
5659843Slinton 	    if (curline > 0) {
5669843Slinton 		printlines(curline, curline);
5679843Slinton 	    } else {
5689843Slinton 		printinst(pc, pc);
5699843Slinton 	    }
5709843Slinton 	    erecover();
5719677Slinton 	} else {
5729843Slinton 	    fixintr();
5739677Slinton 	    isstopped = true;
5749677Slinton 	    printerror();
5759677Slinton 	}
5769677Slinton     }
5779677Slinton }
5789677Slinton 
5799677Slinton /*
58011172Slinton  * Print out the current location in the debuggee.
58111172Slinton  */
58211172Slinton 
58311172Slinton public printloc()
58411172Slinton {
58511172Slinton     printf("in ");
58611172Slinton     printname(stdout, curfunc);
58711172Slinton     putchar(' ');
58814757Slinton     if (curline > 0 and not useInstLoc) {
58911172Slinton 	printsrcpos();
59011172Slinton     } else {
59114757Slinton 	useInstLoc = false;
59214757Slinton 	curline = 0;
59311172Slinton 	printf("at 0x%x", pc);
59411172Slinton     }
59511172Slinton }
59611172Slinton 
59711172Slinton /*
5989677Slinton  * Some functions for testing the state of the process.
5999677Slinton  */
6009677Slinton 
6019677Slinton public Boolean notstarted(p)
6029677Slinton Process p;
6039677Slinton {
6049677Slinton     return (Boolean) (p->status == NOTSTARTED);
6059677Slinton }
6069677Slinton 
6079677Slinton public Boolean isfinished(p)
6089677Slinton Process p;
6099677Slinton {
6109677Slinton     return (Boolean) (p->status == FINISHED);
6119677Slinton }
6129677Slinton 
6139677Slinton /*
6149677Slinton  * Return the signal number which stopped the process.
6159677Slinton  */
6169677Slinton 
6179677Slinton public Integer errnum(p)
6189677Slinton Process p;
6199677Slinton {
6209677Slinton     return p->signo;
6219677Slinton }
6229677Slinton 
6239677Slinton /*
6249677Slinton  * Return the termination code of the process.
6259677Slinton  */
6269677Slinton 
6279677Slinton public Integer exitcode(p)
6289677Slinton Process p;
6299677Slinton {
6309677Slinton     return p->exitval;
6319677Slinton }
6329677Slinton 
6339677Slinton /*
6349677Slinton  * These routines are used to access the debuggee process from
6359677Slinton  * outside this module.
6369677Slinton  *
6379677Slinton  * They invoke "pio" which eventually leads to a call to "ptrace".
63814757Slinton  * The system generates an I/O error when a ptrace fails.  During reads
63914757Slinton  * these are ignored, during writes they are reported as an error, and
64014757Slinton  * for anything else they cause a fatal error.
6419677Slinton  */
6429677Slinton 
6439677Slinton extern Intfunc *onsyserr();
6449677Slinton 
6459677Slinton private badaddr;
64614757Slinton private read_err(), write_err();
6479677Slinton 
6489677Slinton /*
6499677Slinton  * Read from the process' instruction area.
6509677Slinton  */
6519677Slinton 
6529677Slinton public iread(buff, addr, nbytes)
6539677Slinton char *buff;
6549677Slinton Address addr;
6559677Slinton int nbytes;
6569677Slinton {
6579677Slinton     Intfunc *f;
6589677Slinton 
65914757Slinton     f = onsyserr(EIO, read_err);
6609677Slinton     badaddr = addr;
6619677Slinton     if (coredump) {
6629677Slinton 	coredump_readtext(buff, addr, nbytes);
6639677Slinton     } else {
6649677Slinton 	pio(process, PREAD, TEXTSEG, buff, addr, nbytes);
6659677Slinton     }
6669677Slinton     onsyserr(EIO, f);
6679677Slinton }
6689677Slinton 
6699677Slinton /*
6709677Slinton  * Write to the process' instruction area, usually in order to set
6719677Slinton  * or unset a breakpoint.
6729677Slinton  */
6739677Slinton 
6749677Slinton public iwrite(buff, addr, nbytes)
6759677Slinton char *buff;
6769677Slinton Address addr;
6779677Slinton int nbytes;
6789677Slinton {
6799677Slinton     Intfunc *f;
6809677Slinton 
6819677Slinton     if (coredump) {
6829677Slinton 	error("no process to write to");
6839677Slinton     }
68414757Slinton     f = onsyserr(EIO, write_err);
6859677Slinton     badaddr = addr;
6869677Slinton     pio(process, PWRITE, TEXTSEG, buff, addr, nbytes);
6879677Slinton     onsyserr(EIO, f);
6889677Slinton }
6899677Slinton 
6909677Slinton /*
6919677Slinton  * Read for the process' data area.
6929677Slinton  */
6939677Slinton 
6949677Slinton public dread(buff, addr, nbytes)
6959677Slinton char *buff;
6969677Slinton Address addr;
6979677Slinton int nbytes;
6989677Slinton {
6999677Slinton     Intfunc *f;
7009677Slinton 
70114757Slinton     f = onsyserr(EIO, read_err);
7029677Slinton     badaddr = addr;
7039677Slinton     if (coredump) {
7049677Slinton 	coredump_readdata(buff, addr, nbytes);
7059677Slinton     } else {
7069677Slinton 	pio(process, PREAD, DATASEG, buff, addr, nbytes);
7079677Slinton     }
7089677Slinton     onsyserr(EIO, f);
7099677Slinton }
7109677Slinton 
7119677Slinton /*
7129677Slinton  * Write to the process' data area.
7139677Slinton  */
7149677Slinton 
7159677Slinton public dwrite(buff, addr, nbytes)
7169677Slinton char *buff;
7179677Slinton Address addr;
7189677Slinton int nbytes;
7199677Slinton {
7209677Slinton     Intfunc *f;
7219677Slinton 
7229677Slinton     if (coredump) {
7239677Slinton 	error("no process to write to");
7249677Slinton     }
72514757Slinton     f = onsyserr(EIO, write_err);
7269677Slinton     badaddr = addr;
7279677Slinton     pio(process, PWRITE, DATASEG, buff, addr, nbytes);
7289677Slinton     onsyserr(EIO, f);
7299677Slinton }
7309677Slinton 
7319677Slinton /*
73214757Slinton  * Trap for errors in reading or writing to a process.
73314757Slinton  * The current approach is to "ignore" read errors and complain
73414757Slinton  * bitterly about write errors.
7359677Slinton  */
7369677Slinton 
73714757Slinton private read_err()
7389677Slinton {
73911560Slinton     /*
74014757Slinton      * Ignore.
74111560Slinton      */
7429677Slinton }
7439677Slinton 
74414757Slinton private write_err()
74514757Slinton {
74614757Slinton     error("can't write to process (address 0x%x)", badaddr);
74714757Slinton }
74814757Slinton 
7499677Slinton /*
7509677Slinton  * Ptrace interface.
7519677Slinton  */
7529677Slinton 
7539677Slinton /*
7549677Slinton  * This magic macro enables us to look at the process' registers
75514757Slinton  * in its user structure.
7569677Slinton  */
7579677Slinton 
7589677Slinton #define regloc(reg)     (ctob(UPAGES) + ( sizeof(int) * (reg) ))
7599677Slinton 
7609677Slinton #define WMASK           (~(sizeof(Word) - 1))
7619677Slinton #define cachehash(addr) ((unsigned) ((addr >> 2) % CSIZE))
7629677Slinton 
7639677Slinton #define FIRSTSIG        SIGINT
7649677Slinton #define LASTSIG         SIGQUIT
7659677Slinton #define ischild(pid)    ((pid) == 0)
7669677Slinton #define traceme()       ptrace(0, 0, 0, 0)
7679677Slinton #define setrep(n)       (1 << ((n)-1))
7689677Slinton #define istraced(p)     (p->sigset&setrep(p->signo))
7699677Slinton 
7709677Slinton /*
7719677Slinton  * Ptrace options (specified in first argument).
7729677Slinton  */
7739677Slinton 
7749677Slinton #define UREAD   3       /* read from process's user structure */
7759677Slinton #define UWRITE  6       /* write to process's user structure */
7769677Slinton #define IREAD   1       /* read from process's instruction space */
7779677Slinton #define IWRITE  4       /* write to process's instruction space */
7789677Slinton #define DREAD   2       /* read from process's data space */
7799677Slinton #define DWRITE  5       /* write to process's data space */
7809677Slinton #define CONT    7       /* continue stopped process */
7819677Slinton #define SSTEP   9       /* continue for approximately one instruction */
7829677Slinton #define PKILL   8       /* terminate the process */
7839677Slinton 
7849677Slinton /*
7859677Slinton  * Start up a new process by forking and exec-ing the
7869677Slinton  * given argument list, returning when the process is loaded
7879677Slinton  * and ready to execute.  The PROCESS information (pointed to
7889677Slinton  * by the first argument) is appropriately filled.
7899677Slinton  *
7909677Slinton  * If the given PROCESS structure is associated with an already running
7919677Slinton  * process, we terminate it.
7929677Slinton  */
7939677Slinton 
7949677Slinton /* VARARGS2 */
7959677Slinton private pstart(p, argv, infile, outfile)
7969677Slinton Process p;
7979677Slinton String argv[];
7989677Slinton String infile;
7999677Slinton String outfile;
8009677Slinton {
8019677Slinton     int status;
8029677Slinton 
803*16617Ssam     if (p->pid != 0) {
804*16617Ssam 	pterm(p);
8059677Slinton     }
8069677Slinton     psigtrace(p, SIGTRAP, true);
80714395Slinton     p->pid = vfork();
80814395Slinton     if (p->pid == -1) {
8099677Slinton 	panic("can't fork");
8109677Slinton     }
8119677Slinton     if (ischild(p->pid)) {
8129677Slinton 	traceme();
8139677Slinton 	if (infile != nil) {
814*16617Ssam 	    infrom(infile);
8159677Slinton 	}
8169677Slinton 	if (outfile != nil) {
817*16617Ssam 	    outto(outfile);
8189677Slinton 	}
81911832Slinton 	execv(argv[0], argv);
82011172Slinton 	write(2, "can't exec ", 11);
82111172Slinton 	write(2, argv[0], strlen(argv[0]));
82211172Slinton 	write(2, "\n", 1);
82311172Slinton 	_exit(1);
8249677Slinton     }
8259677Slinton     pwait(p->pid, &status);
8269677Slinton     getinfo(p, status);
8279677Slinton     if (p->status != STOPPED) {
8289677Slinton 	error("program could not begin execution");
8299677Slinton     }
83014395Slinton     ptraced(p->pid);
8319677Slinton }
8329677Slinton 
8339677Slinton /*
834*16617Ssam  * Terminate a ptrace'd process.
835*16617Ssam  */
836*16617Ssam 
837*16617Ssam public pterm (p)
838*16617Ssam Process p;
839*16617Ssam {
840*16617Ssam     integer status;
841*16617Ssam 
842*16617Ssam     if (p != nil and p->pid != 0) {
843*16617Ssam 	ptrace(PKILL, p->pid, 0, 0);
844*16617Ssam 	pwait(p->pid, &status);
845*16617Ssam 	unptraced(p->pid);
846*16617Ssam     }
847*16617Ssam }
848*16617Ssam 
849*16617Ssam /*
85011867Slinton  * Continue a stopped process.  The first argument points to a Process
85111867Slinton  * structure.  Before the process is restarted it's user area is modified
85211867Slinton  * according to the values in the structure.  When this routine finishes,
8539677Slinton  * the structure has the new values from the process's user area.
8549677Slinton  *
8559677Slinton  * Pcont terminates when the process stops with a signal pending that
8569677Slinton  * is being traced (via psigtrace), or when the process terminates.
8579677Slinton  */
8589677Slinton 
85911867Slinton private pcont(p, signo)
8609677Slinton Process p;
86111867Slinton int signo;
8629677Slinton {
863*16617Ssam     int s, status;
8649677Slinton 
8659677Slinton     if (p->pid == 0) {
8669677Slinton 	error("program not active");
8679677Slinton     }
868*16617Ssam     s = signo;
8699677Slinton     do {
870*16617Ssam 	setinfo(p, s);
871*16617Ssam 	if (traceexec) {
872*16617Ssam 	    printf("!! pcont from 0x%x with signal %d (%d)\n",
873*16617Ssam 		p->reg[PROGCTR], s, p->signo);
874*16617Ssam 	    fflush(stdout);
875*16617Ssam 	}
8769677Slinton 	sigs_off();
8779677Slinton 	if (ptrace(CONT, p->pid, p->reg[PROGCTR], p->signo) < 0) {
87814395Slinton 	    panic("error %d trying to continue process", errno);
8799677Slinton 	}
8809677Slinton 	pwait(p->pid, &status);
8819677Slinton 	sigs_on();
8829677Slinton 	getinfo(p, status);
883*16617Ssam 	if (traceexec and not istraced(p)) {
884*16617Ssam 	    printf("!! ignored signal %d at 0x%x\n", p->signo, p->reg[PROGCTR]);
885*16617Ssam 	    fflush(stdout);
886*16617Ssam 	}
887*16617Ssam 	s = p->signo;
8889677Slinton     } while (p->status == STOPPED and not istraced(p));
889*16617Ssam     if (traceexec) {
890*16617Ssam 	printf("!! pcont to 0x%x on signal %d\n", p->reg[PROGCTR], p->signo);
891*16617Ssam 	fflush(stdout);
892*16617Ssam     }
8939677Slinton }
8949677Slinton 
8959677Slinton /*
8969677Slinton  * Single step as best ptrace can.
8979677Slinton  */
8989677Slinton 
899*16617Ssam public pstep(p, signo)
9009677Slinton Process p;
901*16617Ssam integer signo;
9029677Slinton {
9039677Slinton     int status;
9049677Slinton 
905*16617Ssam     setinfo(p, signo);
906*16617Ssam     if (traceexec) {
907*16617Ssam 	printf("!! pstep from pc 0x%x with signal %d (%d)\n",
908*16617Ssam 	    p->reg[PROGCTR], signo, p->signo);
909*16617Ssam 	fflush(stdout);
910*16617Ssam     }
9119677Slinton     sigs_off();
912*16617Ssam     if (ptrace(SSTEP, p->pid, p->reg[PROGCTR], p->signo) < 0) {
913*16617Ssam 	panic("error %d trying to step process", errno);
914*16617Ssam     }
9159677Slinton     pwait(p->pid, &status);
9169677Slinton     sigs_on();
9179677Slinton     getinfo(p, status);
918*16617Ssam     if (traceexec) {
919*16617Ssam 	printf("!! pstep to pc 0x%x on signal %d\n", p->reg[PROGCTR], p->signo);
920*16617Ssam 	fflush(stdout);
921*16617Ssam     }
922*16617Ssam     if (p->status != STOPPED) {
923*16617Ssam 	error("program unexpectedly exited with %d\n", p->exitval);
924*16617Ssam     }
9259677Slinton }
9269677Slinton 
9279677Slinton /*
9289677Slinton  * Return from execution when the given signal is pending.
9299677Slinton  */
9309677Slinton 
9319677Slinton public psigtrace(p, sig, sw)
9329677Slinton Process p;
9339677Slinton int sig;
9349677Slinton Boolean sw;
9359677Slinton {
9369677Slinton     if (sw) {
9379677Slinton 	p->sigset |= setrep(sig);
9389677Slinton     } else {
9399677Slinton 	p->sigset &= ~setrep(sig);
9409677Slinton     }
9419677Slinton }
9429677Slinton 
9439677Slinton /*
9449677Slinton  * Don't catch any signals.
9459677Slinton  * Particularly useful when letting a process finish uninhibited.
9469677Slinton  */
9479677Slinton 
9489677Slinton public unsetsigtraces(p)
9499677Slinton Process p;
9509677Slinton {
9519677Slinton     p->sigset = 0;
9529677Slinton }
9539677Slinton 
9549677Slinton /*
9559677Slinton  * Turn off attention to signals not being caught.
9569677Slinton  */
9579677Slinton 
9589677Slinton private Intfunc *sigfunc[NSIG];
9599677Slinton 
9609677Slinton private sigs_off()
9619677Slinton {
9629677Slinton     register int i;
9639677Slinton 
9649677Slinton     for (i = FIRSTSIG; i < LASTSIG; i++) {
9659677Slinton 	if (i != SIGKILL) {
9669677Slinton 	    sigfunc[i] = signal(i, SIG_IGN);
9679677Slinton 	}
9689677Slinton     }
9699677Slinton }
9709677Slinton 
9719677Slinton /*
9729677Slinton  * Turn back on attention to signals.
9739677Slinton  */
9749677Slinton 
9759677Slinton private sigs_on()
9769677Slinton {
9779677Slinton     register int i;
9789677Slinton 
9799677Slinton     for (i = FIRSTSIG; i < LASTSIG; i++) {
9809677Slinton 	if (i != SIGKILL) {
9819677Slinton 	    signal(i, sigfunc[i]);
9829677Slinton 	}
9839677Slinton     }
9849677Slinton }
9859677Slinton 
9869677Slinton /*
9879677Slinton  * Get process information from user area.
9889677Slinton  */
9899677Slinton 
9909677Slinton private int rloc[] ={
9919677Slinton     R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, AP, FP, SP, PC
9929677Slinton };
9939677Slinton 
9949677Slinton private getinfo(p, status)
9959677Slinton register Process p;
9969677Slinton register int status;
9979677Slinton {
9989677Slinton     register int i;
999*16617Ssam     Address addr;
10009677Slinton 
10019677Slinton     p->signo = (status&0177);
10029677Slinton     p->exitval = ((status >> 8)&0377);
10039677Slinton     if (p->signo != STOPPED) {
10049677Slinton 	p->status = FINISHED;
100514757Slinton 	p->pid = 0;
1006*16617Ssam 	p->reg[PROGCTR] = 0;
10079677Slinton     } else {
10089677Slinton 	p->status = p->signo;
10099677Slinton 	p->signo = p->exitval;
10109677Slinton 	p->exitval = 0;
10119677Slinton 	p->mask = ptrace(UREAD, p->pid, regloc(PS), 0);
10129677Slinton 	for (i = 0; i < NREG; i++) {
10139677Slinton 	    p->reg[i] = ptrace(UREAD, p->pid, regloc(rloc[i]), 0);
10149677Slinton 	    p->oreg[i] = p->reg[i];
10159677Slinton 	}
101611768Slinton 	savetty(stdout, &(p->ttyinfo));
1017*16617Ssam 	addr = (Address) &(((struct user *) 0)->u_signal[p->signo]);
1018*16617Ssam 	p->sigstatus = (Address) ptrace(UREAD, p->pid, addr, 0);
10199677Slinton     }
10209677Slinton }
10219677Slinton 
10229677Slinton /*
10239677Slinton  * Set process's user area information from given process structure.
10249677Slinton  */
10259677Slinton 
102611867Slinton private setinfo(p, signo)
10279677Slinton register Process p;
102811867Slinton int signo;
10299677Slinton {
10309677Slinton     register int i;
10319677Slinton     register int r;
10329677Slinton 
103314757Slinton     if (signo == DEFSIG) {
1034*16617Ssam 	if (istraced(p) and (p->sigstatus == 0 or p->sigstatus == 1)) {
103514757Slinton 	    p->signo = 0;
103614757Slinton 	}
103714757Slinton     } else {
103811867Slinton 	p->signo = signo;
10399677Slinton     }
10409677Slinton     for (i = 0; i < NREG; i++) {
10419677Slinton 	if ((r = p->reg[i]) != p->oreg[i]) {
10429677Slinton 	    ptrace(UWRITE, p->pid, regloc(rloc[i]), r);
10439677Slinton 	}
10449677Slinton     }
104511768Slinton     restoretty(stdout, &(p->ttyinfo));
10469677Slinton }
10479677Slinton 
10489677Slinton /*
1049*16617Ssam  * Return the address associated with the current signal.
1050*16617Ssam  * (Plus two since the address points to the beginning of a procedure).
1051*16617Ssam  */
1052*16617Ssam 
1053*16617Ssam public Address usignal (p)
1054*16617Ssam Process p;
1055*16617Ssam {
1056*16617Ssam     Address r;
1057*16617Ssam 
1058*16617Ssam     r = p->sigstatus;
1059*16617Ssam     if (r != 0 and r != 1) {
1060*16617Ssam 	r += 2;
1061*16617Ssam     }
1062*16617Ssam     return r;
1063*16617Ssam }
1064*16617Ssam 
1065*16617Ssam /*
10669677Slinton  * Structure for reading and writing by words, but dealing with bytes.
10679677Slinton  */
10689677Slinton 
10699677Slinton typedef union {
10709677Slinton     Word pword;
10719677Slinton     Byte pbyte[sizeof(Word)];
10729677Slinton } Pword;
10739677Slinton 
10749677Slinton /*
10759677Slinton  * Read (write) from (to) the process' address space.
10769677Slinton  * We must deal with ptrace's inability to look anywhere other
10779677Slinton  * than at a word boundary.
10789677Slinton  */
10799677Slinton 
10809677Slinton private Word fetch();
10819677Slinton private store();
10829677Slinton 
10839677Slinton private pio(p, op, seg, buff, addr, nbytes)
10849677Slinton Process p;
10859677Slinton PioOp op;
10869677Slinton PioSeg seg;
10879677Slinton char *buff;
10889677Slinton Address addr;
10899677Slinton int nbytes;
10909677Slinton {
10919677Slinton     register int i;
10929677Slinton     register Address newaddr;
10939677Slinton     register char *cp;
10949677Slinton     char *bufend;
10959677Slinton     Pword w;
10969677Slinton     Address wordaddr;
10979677Slinton     int byteoff;
10989677Slinton 
10999677Slinton     if (p->status != STOPPED) {
11009677Slinton 	error("program is not active");
11019677Slinton     }
11029677Slinton     cp = buff;
11039677Slinton     newaddr = addr;
11049677Slinton     wordaddr = (newaddr&WMASK);
11059677Slinton     if (wordaddr != newaddr) {
11069677Slinton 	w.pword = fetch(p, seg, wordaddr);
11079677Slinton 	for (i = newaddr - wordaddr; i < sizeof(Word) and nbytes > 0; i++) {
11089677Slinton 	    if (op == PREAD) {
11099677Slinton 		*cp++ = w.pbyte[i];
11109677Slinton 	    } else {
11119677Slinton 		w.pbyte[i] = *cp++;
11129677Slinton 	    }
11139677Slinton 	    nbytes--;
11149677Slinton 	}
11159677Slinton 	if (op == PWRITE) {
11169677Slinton 	    store(p, seg, wordaddr, w.pword);
11179677Slinton 	}
11189677Slinton 	newaddr = wordaddr + sizeof(Word);
11199677Slinton     }
11209677Slinton     byteoff = (nbytes&(~WMASK));
11219677Slinton     nbytes -= byteoff;
11229677Slinton     bufend = cp + nbytes;
11239677Slinton     while (cp < bufend) {
11249677Slinton 	if (op == PREAD) {
11259677Slinton 	    *((Word *) cp) = fetch(p, seg, newaddr);
11269677Slinton 	} else {
11279677Slinton 	    store(p, seg, newaddr, *((Word *) cp));
11289677Slinton 	}
11299677Slinton 	cp += sizeof(Word);
11309677Slinton 	newaddr += sizeof(Word);
11319677Slinton     }
11329677Slinton     if (byteoff > 0) {
11339677Slinton 	w.pword = fetch(p, seg, newaddr);
11349677Slinton 	for (i = 0; i < byteoff; i++) {
11359677Slinton 	    if (op == PREAD) {
11369677Slinton 		*cp++ = w.pbyte[i];
11379677Slinton 	    } else {
11389677Slinton 		w.pbyte[i] = *cp++;
11399677Slinton 	    }
11409677Slinton 	}
11419677Slinton 	if (op == PWRITE) {
11429677Slinton 	    store(p, seg, newaddr, w.pword);
11439677Slinton 	}
11449677Slinton     }
11459677Slinton }
11469677Slinton 
11479677Slinton /*
11489677Slinton  * Get a word from a process at the given address.
11499677Slinton  * The address is assumed to be on a word boundary.
11509677Slinton  *
11519677Slinton  * A simple cache scheme is used to avoid redundant ptrace calls
11529677Slinton  * to the instruction space since it is assumed to be pure.
11539677Slinton  *
11549677Slinton  * It is necessary to use a write-through scheme so that
11559677Slinton  * breakpoints right next to each other don't interfere.
11569677Slinton  */
11579677Slinton 
11589677Slinton private Integer nfetchs, nreads, nwrites;
11599677Slinton 
11609677Slinton private Word fetch(p, seg, addr)
11619677Slinton Process p;
11629677Slinton PioSeg seg;
11639677Slinton register int addr;
11649677Slinton {
11659677Slinton     register CacheWord *wp;
11669677Slinton     register Word w;
11679677Slinton 
11689677Slinton     switch (seg) {
11699677Slinton 	case TEXTSEG:
11709677Slinton 	    ++nfetchs;
11719677Slinton 	    wp = &p->word[cachehash(addr)];
11729677Slinton 	    if (addr == 0 or wp->addr != addr) {
11739677Slinton 		++nreads;
11749677Slinton 		w = ptrace(IREAD, p->pid, addr, 0);
11759677Slinton 		wp->addr = addr;
11769677Slinton 		wp->val = w;
11779677Slinton 	    } else {
11789677Slinton 		w = wp->val;
11799677Slinton 	    }
11809677Slinton 	    break;
11819677Slinton 
11829677Slinton 	case DATASEG:
11839677Slinton 	    w = ptrace(DREAD, p->pid, addr, 0);
11849677Slinton 	    break;
11859677Slinton 
11869677Slinton 	default:
11879677Slinton 	    panic("fetch: bad seg %d", seg);
11889677Slinton 	    /* NOTREACHED */
11899677Slinton     }
11909677Slinton     return w;
11919677Slinton }
11929677Slinton 
11939677Slinton /*
11949677Slinton  * Put a word into the process' address space at the given address.
11959677Slinton  * The address is assumed to be on a word boundary.
11969677Slinton  */
11979677Slinton 
11989677Slinton private store(p, seg, addr, data)
11999677Slinton Process p;
12009677Slinton PioSeg seg;
12019677Slinton int addr;
12029677Slinton Word data;
12039677Slinton {
12049677Slinton     register CacheWord *wp;
12059677Slinton 
12069677Slinton     switch (seg) {
12079677Slinton 	case TEXTSEG:
12089677Slinton 	    ++nwrites;
12099677Slinton 	    wp = &p->word[cachehash(addr)];
12109677Slinton 	    wp->addr = addr;
12119677Slinton 	    wp->val = data;
12129677Slinton 	    ptrace(IWRITE, p->pid, addr, data);
12139677Slinton 	    break;
12149677Slinton 
12159677Slinton 	case DATASEG:
12169677Slinton 	    ptrace(DWRITE, p->pid, addr, data);
12179677Slinton 	    break;
12189677Slinton 
12199677Slinton 	default:
12209677Slinton 	    panic("store: bad seg %d", seg);
12219677Slinton 	    /* NOTREACHED */
12229677Slinton     }
12239677Slinton }
12249677Slinton 
12259677Slinton public printptraceinfo()
12269677Slinton {
12279677Slinton     printf("%d fetchs, %d reads, %d writes\n", nfetchs, nreads, nwrites);
12289677Slinton }
12299677Slinton 
12309677Slinton /*
1231*16617Ssam  * Redirect input.
1232*16617Ssam  * Assuming this is called from a child, we should be careful to avoid
1233*16617Ssam  * (possibly) shared standard I/O buffers.
12349677Slinton  */
12359677Slinton 
1236*16617Ssam private infrom (filename)
1237*16617Ssam String filename;
1238*16617Ssam {
1239*16617Ssam     Fileid in;
1240*16617Ssam 
1241*16617Ssam     in = open(filename, 0);
1242*16617Ssam     if (in == -1) {
1243*16617Ssam 	write(2, "can't read ", 11);
1244*16617Ssam 	write(2, filename, strlen(filename));
1245*16617Ssam 	write(2, "\n", 1);
1246*16617Ssam 	_exit(1);
1247*16617Ssam     }
1248*16617Ssam     fswap(0, in);
1249*16617Ssam }
1250*16617Ssam 
1251*16617Ssam /*
1252*16617Ssam  * Redirect standard output.
1253*16617Ssam  * Same assumptions as for "infrom" above.
1254*16617Ssam  */
1255*16617Ssam 
1256*16617Ssam private outto (filename)
1257*16617Ssam String filename;
1258*16617Ssam {
1259*16617Ssam     Fileid out;
1260*16617Ssam 
1261*16617Ssam     out = creat(filename, 0666);
1262*16617Ssam     if (out == -1) {
1263*16617Ssam 	write(2, "can't write ", 12);
1264*16617Ssam 	write(2, filename, strlen(filename));
1265*16617Ssam 	write(2, "\n", 1);
1266*16617Ssam 	_exit(1);
1267*16617Ssam     }
1268*16617Ssam     fswap(1, out);
1269*16617Ssam }
1270*16617Ssam 
1271*16617Ssam /*
1272*16617Ssam  * Swap file numbers, useful for redirecting standard input or output.
1273*16617Ssam  */
1274*16617Ssam 
12759677Slinton private fswap(oldfd, newfd)
1276*16617Ssam Fileid oldfd;
1277*16617Ssam Fileid newfd;
12789677Slinton {
12799677Slinton     if (oldfd != newfd) {
12809677Slinton 	close(oldfd);
12819677Slinton 	dup(newfd);
12829677Slinton 	close(newfd);
12839677Slinton     }
12849677Slinton }
1285