Interdata_v6/usr/source/chicago/ddt2.c
#
/*
Dynamic Debugging Tool
Bill Allen
Naval Postgraduate School
March, 1975
*/
#include "/usr/sys/param.h"
#include "/usr/sys/user.h"
#include "/usr/sys/reg.h"
extern pinstr();
extern prtop();
extern int fcore;
extern int fsym;
extern int wcore;
extern int wsym;
extern char *gargv[10];
extern int pid;
extern int coroffset;
extern int symoff;
extern char *lp;
extern int errflg;
extern int symlen;
extern int symct;
extern char symbol[8];
extern int symflg;
extern int symval;
char tsym[10];
char fsymbol[10];
extern char ssymbol[8];
extern int ssymflg;
extern int ssymval;
extern char line[128];
extern int regbuf[512];
extern char **uregs;
extern char *rtsize;
int loccsv;
int locsr5;
extern int regloc[];
extern int dot;
extern int tdot;
extern int dotinc;
extern int lastcom;
extern int lastype;
extern char *symfil;
extern char *corfil;
extern int callist[50];
int entpt[50];
int callev;
extern int *callp;
extern int leffect;
extern int headsize; /* length of file header */
extern int tsize; /* length of text segment */
extern int dsize; /* length of data segment */
extern int ssize; /* length of bss segment */
extern int gargc; /* number of arguments from shell */
extern int sdot; /* symbolic operand temporary dot */
extern int ldot;
extern int elastype;
extern int adrfg; /* set if this command had an address */
extern int status;
extern char *symbuf;
extern int *symptr;
#define BREAK 3 /* system call breakpoint */
#define BRKLEN 20
#define CONDLEN 82
extern struct {
int value; /* old value of breakpoint */
int addr; /* address of breakpoint */
char cond[CONDLEN]; /* breakpoint condition */
} brktab[BRKLEN];
extern int brktx;
int symadr;
extern getspsym();
extern putspsym();
extern ssval[];
extern char *ssname[];
extern initssv();
#define RUSER 1
#define RIUSER 2
#define WUSER 4
#define RUREGS 3
#define WUREGS 6
#define SETTRC 0
#define CONTIN 7
#define EXIT 8
#define SSR0 0
#define SSR1 1
#define SSR2 2
#define SSR3 3
#define SSR4 4
#define SSR5 5
#define SSR6 6
#define SSR7 7
#define SSR8 8
#define SSR9 9
#define SSR10 10
#define SSR11 11
#define SSR12 12
#define SSR13 13
#define SSR14 14
#define SSR15 15
#define SSSP 7
#define SSPC 16
#define SSPS 17
#define SS8 18
#define SS9 19
#define SSL 20
#define SSM 21
#define SSQ 22
#define SSA 23
#define SSD 24
#define SSRG 25
#define SSF 26
#define SSSL 27
#define NUMSS 28
/*
* instruction formats
*/
#define SF 1
#define RR 2
#define RX1 3
#define RX2 4
#define RX3 5
#define RI1 6
#define RI2 7
#define BT 010 /* branch instruction (true) */
#define BF 020 /* branch instruction (false) */
#define BBACK 040 /* branch backwards short */
/*
* structure for information about disassembled instruction
*/
extern struct {
int type; /* instruction format */
char op; /* opcode */
char reg[3]; /* registers */
char *disp; /* displacement */
} instr;
getcnt()
{
register t1, t2;
if (*lp != ',')
return(1);
lp++;
t1 = tdot;
if (expr() == 0) {
tdot = t1;
return(1);
}
t2 = tdot;
tdot = t1;
return(t2);
}
cget(n)
{
register w;
/***
w = get(n&0177776); /* use even address **
if (errflg)
reset();
if(n&1) /* wants odd char **
return((w&0177400)>>8);
return(w);
***/
w = get(n & ~01);
if (errflg)
reset();
return(w);
}
printc(c)
{
c =& 0377; /***/
if (c<' ' || c>'}')
printf("\\%o", c);
else
printf("%c", c);
}
expr()
{
register int i,t1,t2, b;
int donef, adrflg, lastop;
symadr=0;
tdot = 0;
adrflg = 0;
lastop = '+';
ssymval = 0;
donef = 0;
loop:
fsymbol[0] = 0;
if (symchar(0)) {
symadr++;
adrflg++;
symcollect('_');
if (*lp++==':' && symchar(0)) {
for (i=0; i<8; i++)
fsymbol[i] = tsym[i];
fsymbol[0] = '~';
symcollect(0);
} else
lp--;
if (symlook(tsym) == 0) {
printf("*** symbol not found\n");
reset();
}
goto loop;
}
if (*lp>='0' && *lp<='9') {
adrflg++;
ssymval = 0;
if (*lp == '0') {
b = 8;
if (*++lp == 'x') {
b = 16;
lp++;
}
}
else
b = 10;
for (;;) {
if ((i = *lp - '0') > 9)
if ((i =- 'a'-'9'-1) < 10)
break;
if (i >= b || i < 0)
break;
ssymval = ssymval * b + i;
lp++;
}
goto loop;
}
if(*lp == '.'){ /* dot */
ssymval = dot;
over:
adrflg++;
lp++;
goto loop;
}
if(*lp == '\''){ /* one ASCII char */
ssymval = *++lp;
goto over;
}
if(*lp == '"'){ /* two ASCII chars */
ssymval = (*++lp)<<8;
ssymval =| *++lp; /* put in second char */
goto over;
}
if(*lp == ':'){ /* special symbol */
adrflg++;
lp++;
ssymval = getspsym();
goto loop;
}
switch (*lp) {
default:
donef++;
case ' ':
case '+':
case '-':
case '*':
case '\\':
case '%':
case '|':
if(*(lp+1) == '|') /* relational */
donef++;
case '&':
if(*(lp+1) == '&') /* relational */
donef++;
case '@':
switch(lastop) {
case '@': /* indirection */
tdot = get(ssymval);
goto op;
case ' ':
case '+':
tdot =+ ssymval;
goto op;
case '*':
tdot = tdot * ssymval;
goto op;
case '\\':
tdot =/ ssymval;
goto op;
case '%':
tdot =% ssymval;
goto op;
case '|':
tdot =| ssymval;
goto op;
case '&':
tdot =& ssymval;
goto op;
case '-':
tdot =- ssymval;
op:
if (donef)
return(adrflg);
else
lastop = *lp++;
}
goto loop;
case '\t':
lp++;
goto loop;
case '(': /* function argument */
lp++;
adrflg++;
t1 = tdot;
ssymval = getarg();
tdot = t1;
goto loop;
case '[':
lp++;
t1 = ssymval;
t2 = tdot;
if (expr() == 0)
tdot = 0;
ssymval = get(t1 + (tdot<<2));
if (errflg)
reset();
tdot = t2;
if (*lp == ']')
lp++;
goto loop;
}
}
symcollect(c)
{
register char *p;
p = tsym;
if (c && !ssval[SSA])
*p++ = c;
while (symchar(1)) {
if (p < &tsym[8])
*p++ = *lp;
lp++;
}
while (p < &tsym[8])
*p++ = 0;
}
symchar(dig)
{
if (*lp>='a'&&*lp<='z' || *lp>='A'&&*lp<='Z' || *lp=='_')
return(1);
if (dig && *lp>='0' && *lp<='9')
return(1);
return(0);
}
setstack()
{
register int tpc, i;
int trbase;
tpc = ssval[SSPC];
trbase = ssval[SSR14];
callev = 0;
while (errflg == 0) {
findroutine(tpc, &trbase);
tpc = get(trbase+28);
if (callev >= 50)
break;
entpt[callev] = ssymval;
callist[callev++] = trbase;
if ((trbase = get(trbase+24)) == 0)
break;
}
errflg = 0;
setfunc();
}
getarg()
{
register level, arg, t1;
t1 = tdot;
expr();
level = tdot;
if (*lp++ != ',')
error();
expr();
arg = tdot;
if (*lp++ != ')')
error();
if (level >= callp-callist)
error();
ssymval = callist[level] - 8 - 4*arg;
tdot = t1;
}
error()
{
printf("** invalid function argument specifier\n");
reset();
}
printtrace()
{
register int tpc,narg;
int trbase;
int argp, i;
if(fcore<0 && pid==0){ /* no core file */
printf("no core image file\n");
return;
}
tpc = ssval[SSPC];
trbase = ssval[SSR14];
callp = &callist[0];
while (errflg == 0) {
narg = findroutine(tpc, &trbase)+2;
printf("%2d: %.8s(", callp-callist, ssymbol);
if (--narg >= 0)
printf("%o", get(trbase+32));
argp = trbase+32;
while(--narg >= 0) {
printf(",%o", get(argp =+ 4));
}
printf(")\n");
tpc = get(trbase+28);
if (callp < &callist[50])
*callp++ = trbase;
if ((trbase = get(trbase+24)) == 0)
break;
}
}
findroutine(apc, arbase)
int *arbase;
{
register callpt, inst, narg;
if (findbase(apc, arbase) == 0
&& findbase(ssval[SSR15], arbase) == 0) {
errflg++;
return(0);
}
callpt = get(*arbase+28);
if ((inst=get(callpt-6)) >> 24 == 0101) /* rx3 */
inst = get(callpt-4) & 03777777;
else if (((inst>>8) & 0377) == 0101) { /* rx1, rx2 */
inst = get(callpt-4) & 0177777;
if (inst & 0100000) { /* rx2 */
if (inst & 040000)
inst =| ((-1)<<16);
else
inst =& 037777;
inst =+ callpt;
}
}
else {
errflg++;
/*** printf("*** unable to set stack\n"); ***/
return(0);
}
inst = vallook(inst);
if (inst) {
ssymbol[0] = '?';
ssymbol[1] = 0;
ssymval = 0;
}
inst = get(callpt);
if ((inst>>20) == 01147) /* ais sp,x */
return(((inst>>16)&15)/4);
if ((inst>>20) == 06247) /* ahi sp,x */
return((inst&0177777)/4);
if ((inst>>20) == 07647) /* ai sp,x */
return(get(callpt+2)/4);
return(0);
}
/*
* Adjust stack base register to point to saved regs
*/
#define SHI 0xcb
#define SI 0xfb
#define STM 0xd0
findbase(apc, arbase)
int *arbase;
{
register loc;
vallook(apc); /* name of current function */
loc = ssymval; /* entry pt of current function */
loc =+ disasm(loc); /* skip SI */
if (instr.op != SI && instr.op != SHI)
return(0);
loc =+ disasm(loc); /* check STM */
if (instr.op != STM || apc < loc+2)
return(0);
*arbase =+ instr.disp; /* adjust stack base by offset in STM */
return(1);
}
symlook(symstr)
char *symstr;
{
register i;
register symv;
symset();
if (fsymbol[0]==0) {
while(symget()) {
if (eqstr(symbol, symstr)) {
savsym();
return(1);
}
}
if(*symstr == '_') { /* look for a local symbol */
symset();
while(symget()) {
if(symbol[0]!='~' || symval!=ssval[SSF])
continue;
while(symget() && symbol[0]!='~' && symflg!=037)
if(eqstr(symbol,symstr+1))
return(localsym(ssval[SSF]));
return(0);
}
}
return(0);
}
while (symget()) {
/* wait for function symbol */
if (symbol[0]!='~' || !eqstr(symbol, fsymbol))
continue;
symv = symval;
while (symget()&& symbol[0]!='~' &&symflg!=037)
if (eqstr(symbol, symstr))
return(localsym(symv));
return(0);
}
}
localsym(s)
{
register i, xr5;
/* label, static */
if (symflg>=2 && symflg<=4) {
ssymval = symval;
return(1);
}
/* auto, arg */
if (symflg==1) {
for (i=0; i<callev; i++)
if (entpt[i]==s) {
ssymval = symval+callist[i];
return(1);
}
return(0);
}
/* register */
if (symflg==20) {
for (i=0; i<callev; i++)
if (entpt[i]==s) {
if (i==0) {
return(0); /* temp, no reg lvalue */
}
ssymval = callist[i-1] - 10 + 2*symval;
return(1);
}
return(0);
}
return(0);
}
eqstr(as1, as2)
int *as1, *as2;
{
register char *s1, *s2, *es1;
s1 = as1;
s2 = as2;
for (es1 = s1+8; s1 < es1; )
if (*s1++ != *s2++)
return(0);
return(1);
}
vallook(value)
char *value;
{
register char *diff;
diff = 0177777;
symset();
while (symget())
if (symflg&040 && value>=symval && value-symval<=diff) {
if (symflg==1 && value!=symval)
continue;
savsym('_');
diff = value-symval;
}
return(diff);
}
get(aaddr)
char *aaddr;
{
int w;
register a, i;
register char *addr;
addr = aaddr&~01;
if (addr&02) /* not on word boundary */
return((get(addr-2)<<16) | (get(addr+2)>>16));
for(i=0;i<=brktx;i++)
if((brktab[i].addr&~03) == addr)
return(brktab[i].value);
if(pid) {
return(ptrace(RUSER,pid,addr,0));
}
w = 0;
if(gargc>3){ /* file is not core image */
seek(fcore,addr,0);
if(read(fcore,&w,4) != 4)
printf("** unable to read core file\n");
return(w);
}
if (addr < tsize) {
if(fcore>0) goto rdcore;
rdsym: seek(fsym, addr+040, 0);
if (read(fsym, &w, 4) != 4)
printf("** unable to read a.out file\n");
return(w);
}
if (addr < rtsize+dsize) {
if(fcore<0) goto rdsym; /* no core file */
addr =- rtsize;
}
else if (addr > (14<<16) && addr < (14<<16)+ssize)
addr =+ dsize - (14<<16);
else
printf("** invalid address\n");
if(fcore<0){
printf("** can't access bss - no core file\n");
return(0);
}
rdcore:
seek(fcore, addr+03000, 0);
if (read(fcore, &w, 4) < 4)
printf("can't read core file\n");
return(w);
}
symset()
{
symct = symlen;
symptr = symbuf;
}
/*
* convert symbols from symbol table into lowercase
*/
symget()
{
register int *p, *q;
register char *s;
register c;
if ((symct =- 16) < 0) /***/
return(0);
p = symptr;
for (q=symbol; q <= &symval; q++)
*q = *p++;
symptr = p;
/*** Fudge for Interdata assembler which had only uppercase symbols
for (s=symbol; c = *s; s++) {
if (s >= &symbol[8])
break;
if (c >= 'A' && c <= 'Z')
*s = (c =+ 'a'-'A');
}
***/
return(1);
}
savsym(skip)
{
register int ch;
register char *p, *q;
p = symbol;
q = ssymbol;
if(!ssval[SSA] && (*p==skip || (skip=='_' && *p=='~')))
p++;
while (p<symbol+8 && (ch = *p++)) {
*q++ = ch;
}
while (q < ssymbol+8)
*q++ = '\0';
ssymflg = symflg;
ssymval = symval;
ch = symflg&07;
if(ch==3 || ch==4) /* data or bss */
ssymval =+ ssval[SSD]; /* adjust for D space offset */
}
onintr()
{
putchar('\n');
errflg++;
reset();
}
/* print a local symbol if possible */
plocsym(addr)
{
register flg;
symset(); /* beginning of symbol table */
flg = 0;
while(symget()) {
if(symbol[0] == '~' ) {
if(symval <= dot)
continue;
else
break;
}
else
if((symflg&077) == 1 && symval==addr) {
savsym(0);
flg++;
}
}
if(flg)
printf("%.8s",ssymbol);
else
printf("%o(r5)",addr);
}
/* print dot symbolicly if possible */
psym(addr)
{
register int offset;
if(leffect==0) /* need to save effective address */
leffect = addr;
if(addr>ssval[SSL] || addr<0) /* lowest address to use as a symbol */
if((offset=closeval(addr))>=0){ /* find a close symbol */
if(offset>0)
printf("%.8s+%o",ssymbol,offset);
else
printf("%.8s",ssymbol);
return;
}
printf("%o",addr); /* no close symbol */
}
/* find a symbol whose value is close to the given value */
closeval(tvalue)
{
register int tcval,value;
int dflag;
register int t;
value = tvalue;
dflag = 1;
t = ssval[SSD];
if(t) { /* D space offset defined */
/* the following complex code determines if value>=t in unsigned */
/* 16 bit addresses using signed 16 bit arithmetic. */
if(value>=0){
if(t>0 && value>=t){ /* true */
value =- t; /* convert to symbol table address */
dflag=0; /* D space symbol */
}
}
else { /* value<0 */
if(t>=0){
value = (value&077777) + (077777-t) + 1;
dflag = 0;
}
else if(value>=t){ /* both are negative */
value = (value&077777) - (t&077777);
dflag = 0;
}
}
}
if(value<0 && value>=ssval[SSSL]) /* on the stack */
return(closeloc(value)); /* local symbol */
tcval=ssval[SSRG]; /* set close symbol range */
symset(); /* beginning of symbol table */
while(symget()) {
t = symflg&037; /* symbol type */
if(t != 037 && (t!=1 || ssval[SSA])){ /* no file names not abs or assmebly */
if(ssval[SSD]==0 || (dflag && t!=3 && t!=4) || (!dflag &&
(t==3 || t==4))) { /* for system I&D split */
if(value == symval){ /* found it */
savsym('_');
return(0);
}
/* find the signed difference between the two sixteen bit addresses */
if(value>=0 && symval<0)
t = -(symval-value);
else
t = value-symval;
if(t>0 && t<tcval){
tcval = t; /* offset value */
savsym('_');
}
}
}
}
if(tcval==ssval[SSRG]) /* didnt find one close */
return(-1);
return(tcval);
}
/* try to find a local symbol close to value */
/* return the offset */
closeloc(tvalue)
{
register value, offset, t;
offset = ssval[SSRG]; /* symbol range */
for(t=0; t<callev; t++)
if(entpt[t] == ssval[SSF]) {
value = tvalue-callist[t];
break;
}
symset();
while(symget()) {
if(symbol[0]!='~' || symval!=ssval[SSF])
continue;
while(symget() && symbol[0]!='~' && symflg!=037) {
if(value==symval) {
savsym('_');
return(0); /* direct hit */
}
t = symval - value;
if(t>0 && t<offset) {
offset = t;
savsym('_');
}
}
if(offset==ssval[SSRG])
return(-1);
return(offset);
}
return(-1);
}
/* store value in the file in the word addressed by dot. */
/* use the core file if it exists. otherwise use the a.out file. */
cfput(value)
{
register int addr,i,seektyp;
seektyp = 0;
addr = (dot&~01); /* address in file */
if(pid) {
if (addr&02) {
printf("** not on word boundary\n");
return;
}
if(ptrace(WUSER,pid,addr,value) == -1) {
printf("Unable to write child memory\n");
printf("addr = %o value = %o\n",addr,value);
}
return;
}
if(gargc>3){ /* not a core image */
if(wcore<0){
cwf: printf("no write access to core file\n");
return;
}
if(seek(wcore,addr,0)<0)
goto cwf;
if(write(wcore,&value,4) != 4)
goto cwf;
return;
}
if(wcore>0){ /* can write on core */
seek(wcore,addr+03000,1); /* desired address */
if(write(wcore,&value,4) != 4)
printf("no write access to core file\n");
}
seek(wsym,addr+040,0); /* write on symbollic file */
if(write(wsym,&value,4) != 4)
printf("no write access %s\n",symfil);
}
/* clear one or all breakpoints */
clbkpt(adr)
{
register int i,found;
found=0;
if(brktx<0) return;
for(i=0;i<=brktx;i++){ /* thru brkpt table */
if((!adrfg || brktab[i].addr==adr) && brktab[i].addr != -1){
dot = brktab[i].addr&~03; /* address to reset */
cfput(brktab[i].value); /* reset value */
brktab[i].value = -1;
brktab[i].addr = -1;
found++;
}
}
if(found)
putchar('\n');
else if(adrfg)
printf("?? no breakpoint\n");
dot = ldot; /* reset dot */
return;
}
/* initialize known symbols from the core file */
initfcor()
{
register t,i ;
if(fcore<0) {
printf("cant initialize from fcore\n");
return;
}
if(seek(fcore,coroffset,3)<0){ /* beginning of core file */
printf("fcore seek error\n");
endit();
}
if(read(fcore, regbuf, 03000)<03000) {
printf("fcore read error\n");
endit();
}
t = regbuf->u_ar0;
t =- (14<<16);
uregs = ®buf[t/4];
status = (regbuf->u_arg[0]);
tsize=(regbuf->u_tsize)<< 8;
dsize=(regbuf->u_dsize)<< 8;
ssize=(regbuf->u_ssize) << 8;
rtsize = (regbuf->u_tsize+0177777) & ~0177777;
headsize = 03000; /* core file header */
/* copy user regs to special symbols */
for (i = SSR0; i <= SSPS; i++)
ssval[i] = uregs[regloc[i]];
/***
if(pid>0) /* at a breakpoint **
ssval[SSPC] =- 2; /* .-2 is real instr address **
***/
}
/* put special symbol values back in core file */
restcore()
{
register i;
for(i=SSR0; i<SSPS; i++) /* restore special symbols */
ptrace(WUREGS,pid,regbuf->u_ar0+regloc[i],ssval[i]);
}
/* get the registers from breakpointed child */
initfmem()
{
register i;
regbuf->u_ar0 = ptrace(RUREGS, pid, &0->u_ar0, 0);
regbuf->u_ar0 =- (14<<14);
for(i=SSR0; i<SSPS; i++)
ssval[i] =
ptrace(RUREGS,pid,regbuf->u_ar0+regloc[i],0);
/***
if(pid) {
ssval[SSPC] =- 2;
ssval[SSPS] =& ~020;
}
***/
}
endit()
{
if(pid>0) {
ptrace(EXIT,pid,0,0);
pid = 0;
}
adrfg = 0;
clbkpt(0); /* clear all breakpoints */
exit();
}
/* init file segment sizes from symbolic file */
initfsym()
{
seek(fsym,0,0); /* beginning */
read(fsym, regbuf, 040);
if (regbuf[0]!=0410 && regbuf[0]!=0407 && regbuf[0]!=0411) {/* magic */
printf("Not a.out format: %s\n", symfil);
gargc = 4;
fcore = fsym;
wcore = wsym;
return;
}
symoff = regbuf[1] + regbuf[2];
symlen = regbuf[4];
if (regbuf[7] != 1)
symoff =<< 1;
symoff =+ 040;
tsize = regbuf[1]; /* text size */
dsize = regbuf[2]; /* data size */
ssize = 0; /* no bss */
headsize = 040; /* a.out header size */
rtsize = tsize;
}
/* set the default current function */
setfunc()
{
/***
symset();
while(symget()) {
if(symbol[0] == '~') {
if(symval <= ssval[SSPC])
ssval[SSF] = symval;
else
break;
}
}
***/
vallook(ssval[SSPC]);
ssval[SSF] = ssymval;
}
/* check for conditional breakpoint */
/* return 0 if no break */
/* !0 if break */
condbpt()
{
register bpx;
for(bpx=0; bpx<BRKLEN; bpx++)
if(brktab[bpx].addr == ssval[SSPC]) {
if(brktab[bpx].cond[0] == '\0')
return(1); /* unconditional breakpoint */
else
return(evalcond(bpx));
}
return(1); /* not in break table */
}
/* evaluate conditinal expression */
/* return */
/* 0 if false */
/* !0 if true */
relexpr()
{
register condv,nextrel;
int adrflg;
condv=0;
adrflg = expr();
if(errflg || adrflg==0) {
err:
printf("*** invalid breakpoint condition\n");
errflg++;
return(1);
}
if(symadr)
tdot = cget(tdot);
condv = tdot;
switch(*lp++) {
default:
return(condv);
case '=':
if(*lp != '=')
goto err;
lp++;
nextrel = 2;
break;
case '!':
if(*lp != '=')
goto err;
lp++;
nextrel = 3;
break;
case '<':
if(*lp == '=') {
lp++;
nextrel = 5;
}
else
nextrel = 4;
break;
case '>':
if(*lp == '=') {
lp++;
nextrel = 7;
}
else
nextrel = 6;
break;
}
if(expr()==0 || errflg!=0)
goto err;
if(symadr)
tdot = cget(tdot);
switch(nextrel) {
case 2: /* == */
condv = (condv == tdot);
break;
case 3: /* != */
condv = (condv != tdot);
break;
case 4: /* < */
condv = (condv < tdot);
break;
case 5: /* <= */
condv = (condv <= tdot);
break;
case 6: /* > */
condv = (condv > tdot);
break;
case 7: /* >= */
condv = (condv >= tdot);
break;
}
return(condv);
}
evalcond(bpx)
{
register lval,rval;
lp = &brktab[bpx].cond[0];
lval = relexpr();
while(errflg == 0) {
switch(*lp++) {
default:
if(*lp != '\0')
goto err;
lp = line;
return(lval);
case '|':
if(*lp != '|')
goto err;
lp++;
rval = relexpr();
lval = (lval || rval);
break;
case '&':
if(*lp != '&')
goto err;
lp++;
rval = relexpr();
lval = (lval && rval);
break;
}
}
err:
printf("*** invalid breakpoint condition\n");
return(1);
}