
static char acRcsSpt[] =
    "$Header: spt.c,v 1.43 96/01/11 17:55:12 k2mm Exp $";

/*
    clones: detect by NoMatch, maybe another settable bit, too?
    + for death by cloning, * for birth by cloning.
*/

#define CASE_NONE	0
#define CASE_TXSTART	1
#define CASE_RXSTART	2
#define CASE_PASS	3
#define CASE_BUSTPASS	4
#define CASE_CHG	5
#define CASE_BUSTCHG	6
#define CASE_END	7
#define CASE_BUSTEND	8
#define CASE_LOST	9

#define TR_PQ( x, y)	{   if( y)					\
			    {   printf( "%s%d%d:\t", x,			\
				    ((y)->iFlags & mTXTRC) != 0,	\
				    ((y)->iFlags & mRXTRC) != 0);	\
				PrintQso( y, 0);			\
			    }						\
			}

#define TR_TIME( pqso)	if( iTrTime) printf( " :%04d", \
			    iTimeToHrMin( iADJTIME( pqso)))

typedef struct trc *	pTRC;
typedef struct trc
{   pQSO		pqsoOrig;
    pQSO		pqsoPrev;
    pQSO		pqsoThis;
    int			iOrigPrinted;
    int			iThisPrinted;
}   TRC;

typedef struct fun *	pFUN;
typedef struct fun
{   int			(*pfFun)();
    TEXT		tName;
    TEXT		tExplain;
}   FUN;

pQSO			pqsoTrHead;
pQSO			pqsoTrTxFirst;
pQSO			pqsoTrRxFirst;
pQSO			pqsoTrTxLast;
pQSO			pqsoTrRxLast;
int			iTrTime;

extern FUN		afun[];
extern pFUN		pfunEnd;
pFILE			pfCmd;

pQSO
pqsoFindTxNrCall( pcallTx, iTxNr, pcallWkd)
pCALL			pcallTx, pcallWkd;
int			iTxNr;
{   pQSO		pqso;

    if( pcallTx == 0)
	return 0;
    for( pqso = pcallTx->pqsoTxHead; pqso; pqso = pqso->pqsoTxNext)
	if( pqso->iTxNr == iTxNr && pqso->pcallRx == pcallWkd)
	    return pqso;
    return 0;
}

pQSO
pqsoFindRxNrCall( pcallRx, iRxNr, pcallWkd)
pCALL			pcallRx, pcallWkd;
int			iRxNr;
{   pQSO		pqso;

    if( pcallRx == 0)
	return 0;
    for( pqso = pcallRx->pqsoRxHead; pqso; pqso = pqso->pqsoRxNext)
	if( pqso->iRxNr == iRxNr && pqso->pcallTx == pcallWkd)
	    return pqso;
    return 0;
}

pQSO
pqsoFindCheckRxNr( pcallRx, iRxNr)
pCALL			pcallRx;
int			iRxNr;
{   int			nFind;
    pQSO		pqso, pqsoFind;

    nFind = 0;
    for( pqso = pcallRx->pqsoRxHead; pqso; pqso = pqso->pqsoRxNext)
	if( pqso->iRxNr == iRxNr)
	{   pqsoFind = pqso;
	    nFind++;
	}
    if( nFind == 1)
    {   pqso = pqsoFind->pqsoMatch;
	if( pqso && pqso->iTxNr != iRxNr)
	{   printf( "warning: QSO-NR mismatch:\n");
	    TR_PQ( "rx", pqsoFind)
	    TR_PQ( "tx", pqsoFind->pqsoMatch)
	}
	return pqsoFind;
    }
    else if( nFind > 1)
    {   printf( "ambiguous:\n");
	for( pqso = pcallRx->pqsoRxHead; pqso; pqso = pqso->pqsoRxNext)
	    if( pqso->iRxNr == iRxNr)
	    {   TR_PQ( "rx", pqso)
		if( pqso->pqsoMatch)
		    TR_PQ( "tx", pqso->pqsoMatch)
	    }
    }
    return 0;
}

pQSO
pqsoFindTxNamePrev( pqsoRx)
pQSO			pqsoRx;
{   pCALL		pcallNon;
    pQSO		pqsoTx, pqsoTxMax;
    int			iProb, iProbMax;

    pcallNon = pqsoRx->pcallRx;
    if((pcallNon->iFlags & opFLAKE) == 0)
	return pqsoFindRxNr( pcallNon, pqsoRx->iRxNr - 1);
    iProbMax = -1000;
    pqsoTxMax = 0;
    for( pqsoTx = pcallNon->pqsoRxHead; pqsoTx; pqsoTx = pqsoTx->pqsoRxNext)
	if( pqsoTx->iTxNr == pqsoRx->iRxNr)
	{   if( pqsoTx->pqsoTxSkip)
		if( pqsoTx->pqsoTxSkip == pqsoRx)
		    return pqsoTx;
		else
		    continue;
	    iProb = iProbQsoLink( pqsoTx, pqsoRx);
	    if( iProbMax < iProb)
	    {   iProbMax = iProb;
		pqsoTxMax = pqsoTx;
	    }
	}
    return pqsoTxMax;
}

pQSO
pqsoFindRxNameNext( pqsoTx)
pQSO			pqsoTx;
{   pCALL		pcallNon;
    pQSO		pqsoRx, pqsoRxMax;
    int			iNr, iProb, iProbMax;

    pcallNon = pqsoTx->pcallRx;
    iNr = pcallNon->iFlags & opFLAKE ? pqsoTx->iTxNr : pqsoTx->iRxNr + 1;
    iProbMax = -1000;
    pqsoRxMax = 0;
    for( pqsoRx = pcallNon->pqsoRxHead; pqsoRx; pqsoRx = pqsoRx->pqsoRxNext)
	if( pqsoRx->iRxNr == iNr)
	{   if( pqsoRx->pqsoRxSkip)
		if( pqsoRx->pqsoRxSkip == pqsoTx)
		    return pqsoRx;
		else
		    continue;
	    iProb = iProbQsoLink( pqsoTx, pqsoRx);
	    if( iProbMax < iProb)
	    {   iProbMax = iProb;
		pqsoRxMax = pqsoRx;
	    }
	}
    return pqsoRxMax;
}

int
iProbQsoLink( pqsoTx, pqsoRx)
pQSO			pqsoTx, pqsoRx;
{   int			iDelay, iMatch;

    if( pqsoRx->iTime == iTIMEUNK || pqsoTx->iTime == iTIMEUNK)
	iDelay = 10;
    else
	iDelay = iADJTIME( pqsoRx) - iADJTIME( pqsoTx);
    iMatch = pqsoTx->pstrTxName == pqsoRx->pstrRxName;
    return iDelay * (iDelay < 0 ? 3 : -1) + iMatch * 10;
}

NameCheckCall( ptrc, ptOrigCall, ptPrevCall, ptMyCall, ptJumpCall)
pTRC			ptrc;
pTEXT			ptOrigCall, ptPrevCall, ptMyCall, ptJumpCall;
{   pQSO		pqsoOrig = ptrc->pqsoOrig;
    pQSO		pqsoPrev = ptrc->pqsoPrev;
    pQSO		pqsoThis = ptrc->pqsoThis;

    *ptOrigCall = pqsoOrig ? pqsoOrig->pcallTx->pstrCall->ac : 0;

    *ptPrevCall = pqsoOrig ? pqsoOrig->pcallRx->pstrCall->ac :
	          pqsoPrev ? pqsoPrev->pcallRx->pstrCall->ac : 0;

    *ptMyCall   = pqsoPrev ? pqsoPrev->pcallTx->pstrCall->ac :
	          pqsoThis ? pqsoThis->pcallTx->pstrCall->ac : 0;

    *ptJumpCall = pqsoOrig == 0 ? 0 :
		  pqsoOrig->pstrTxJump == 0 ? "" :
		  pqsoOrig->pstrTxJump->ac;
}

NameCheckCond( ptrc, piSkip, piBust, piChg, piEnd)
pTRC			ptrc;
pINT			piSkip, piBust, piChg, piEnd;
{   pQSO		pqsoOrig = ptrc->pqsoOrig;
    pQSO		pqsoPrev = ptrc->pqsoPrev;
    pQSO		pqsoThis = ptrc->pqsoThis;

    *piSkip = pqsoOrig
	&& (pqsoOrig->pqsoTxSkip || pqsoOrig->pqsoMatch == 0)
	&& (pqsoOrig->pqsoTxSkip || pqsoOrig->pcallRx->pqsoTxHead == 0);

    *piBust = pqsoOrig && pqsoPrev &&
	pqsoOrig->pstrTxName != pqsoPrev->pstrRxName;

    *piChg = pqsoPrev && pqsoThis &&
	pqsoPrev->pstrRxName != pqsoThis->pstrTxName;

    *piEnd = pqsoThis == 0;
}

int
iNameCheckCase( ptrc, iBust, iChg, iEnd)
pTRC			ptrc;
int			iBust, iChg, iEnd;
{   pQSO		pqsoOrig = ptrc->pqsoOrig;
    pQSO		pqsoPrev = ptrc->pqsoPrev;
    pQSO		pqsoThis = ptrc->pqsoThis;
    int			iCode, iCase;

    iCode =
	(pqsoOrig ? 4 : 0) +
	(pqsoPrev ? 2 : 0) +
	(pqsoThis ? 1 : 0) ;
    switch( iCode)
    {   case 0: iCase = CASE_NONE;	break;
	case 1: iCase = CASE_TXSTART;	break;
	case 2:
	case 3: iCase = CASE_RXSTART;	break;
	case 4:
	case 5: iCase = CASE_LOST;	break;
	case 6:
	case 7: iCase =
		    iEnd ? CASE_END :
		    iChg ? CASE_CHG :
			   CASE_PASS;
		if( iBust) iCase += 1;	break;
    }
    return iCase;
}

NamePrint( pqso, pcall, iNr, tName)
pQSO			pqso;
pCALL			pcall;
int			iNr;
TEXT			tName;
{
    printf( "\n");
    TR_TIME( pqso);
    printf( acFmtCall, pcall->pstrCall->ac);
    if( iNr)
	printf( acFmtNrInt, iNr);
    else
	printf( acFmtNrStr, "");
    printf( acFmtName, tName);
    printf( " ");
}

NamePrintTx( pqso)
pQSO			pqso;
{
    NamePrint( pqso, pqso->pcallTx, pqso->iTxNr, pqso->pstrTxName->ac);
}

NamePrintRx( pqso)
pQSO			pqso;
{
    NamePrint( pqso, pqso->pcallRx, pqso->iRxNr, pqso->pstrRxName->ac);
}

NamePrintEnd( pqso)
pQSO			pqso;
{
    NamePrint( pqso, pqso->pcallTx, pqso->iTxNr, pqso->pstrRxName->ac);
}

NameCheckAndPrint( ptrc)
pTRC			ptrc;
{   pQSO		pqsoOrig = ptrc->pqsoOrig;
    pQSO		pqsoPrev = ptrc->pqsoPrev;
    pQSO		pqsoThis = ptrc->pqsoThis;
    TEXT		tOrigCall, tPrevCall, tMyCall, tJumpCall;
    int			iCase, iSkip, iBust, iChg, iEnd, iNoClue, iClone;
    pQSO pqsoOther;

    NameCheckCall( ptrc, &tOrigCall, &tPrevCall, &tMyCall, &tJumpCall);

    NameCheckCond( ptrc, &iSkip, &iBust, &iChg, &iEnd);

    switch( iNameCheckCase( ptrc, iBust, iChg, iEnd))
    {   default:
	case CASE_NONE:
	    return;
	case CASE_TXSTART:
	    pqsoPrev = pqsoThis->pqsoTxPrev;
	    NamePrintTx( pqsoThis);
	    iClone = pqsoPrev && (
		(pqsoPrev->iFlags & mCLONE)
		|| pqsoPrev->pqsoMatch == 0
		    && (pqsoOther = pqsoFindTxNr( pqsoPrev->pcallRx, pqsoPrev->iRxNr))
	    );
	    if( iClone) printf( "*");
	    ptrc->iThisPrinted = 1;
	    break;
	case CASE_RXSTART:
	    pqsoOrig = pqsoPrev->pqsoMatch ? pqsoPrev->pqsoMatch->pqsoTxPrev : 0;
	    NamePrintRx( pqsoPrev);
	    iClone = pqsoOrig && (
		(pqsoOrig->iFlags & mCLONE)
		|| pqsoOrig->pqsoMatch == 0
		    && pqsoFindTxNr( pqsoOrig->pcallRx, pqsoOrig->iRxNr)
	    );
	    printf( iClone ? "*=" : "=");
	    if( iChg)
	    {   printf( "%s", tMyCall);
		TR_TIME( pqsoPrev);
		NamePrintTx( pqsoThis);
		ptrc->iThisPrinted = 1;
	    }
	    if( iEnd)
		printf( "%s.", tMyCall);
	    break;
	case CASE_PASS:
	    if( iSkip) printf( tJumpCall && *tJumpCall ? "-?-" : "-");
	    printf( "=");
	    break;
	case CASE_BUSTPASS:
	    if( ! ptrc->iOrigPrinted) printf( "%s", tOrigCall);
	    if( iSkip)
	    {   printf( "-%s%s", tPrevCall, tJumpCall);
		iNoClue = (pqsoPrev->pcallRx->iFlags & opNOCLUE)
		    && pqsoPrev->pcallRx->pstrName == pqsoPrev->pstrRxName;
		printf( iNoClue ? "#" : "=");
	    }
	    else
		printf( "=");
	    TR_TIME( pqsoPrev);
	    NamePrintTx( pqsoThis);
	    ptrc->iThisPrinted = 1;
	    break;
	case CASE_CHG:
	    if( iSkip) printf( tJumpCall && *tJumpCall ? "-?-" : "-");
	    printf( "=%s", tMyCall);
	    TR_TIME( pqsoPrev);
	    NamePrintTx( pqsoThis);
	    ptrc->iThisPrinted = 1;
	    break;
	case CASE_BUSTCHG:
	    if( ! ptrc->iOrigPrinted) printf( "%s", tOrigCall);
	    if( iSkip) printf( "-%s%s", tPrevCall, tJumpCall);
	    printf( "=");
	    TR_TIME( pqsoPrev);
	    NamePrintEnd( pqsoPrev);
	    NamePrintTx( pqsoThis);
	    ptrc->iThisPrinted = 1;
	    break;
	case CASE_END:
	    if( iSkip) printf( tJumpCall && *tJumpCall ? "-?-" : "-");
	    printf( "=%s.", tMyCall);
	    TR_TIME( pqsoPrev);
	    break;
	case CASE_BUSTEND:
	    if( ! ptrc->iOrigPrinted) printf( "%s", tOrigCall);
	    if( iSkip) printf( "-%s%s", tPrevCall, tJumpCall);
	    printf( "=");
	    NamePrintEnd( pqsoPrev);
	    printf( ".");
	    TR_TIME( pqsoPrev);
	    break;
	case CASE_LOST:
	    if( ! ptrc->iOrigPrinted) printf( "%s", tOrigCall);
	    if( iSkip)
	    {   printf( "-%s%s", tPrevCall, tJumpCall);
		iNoClue = pqsoOrig->pcallRx->iFlags & opNOCLUE;
		printf( iNoClue ? "#" : "?");
	    }
	    else if
	    (   (pqsoOrig->iFlags & mCLONE)
		|| pqsoFindTxNr( pqsoOrig->pcallRx, pqsoOrig->iRxNr)
	    )
		printf( "+%s", tPrevCall);
	    else
		printf( "=%s?", tPrevCall);
	    TR_TIME( pqsoOrig);
	    break;
    }
    if( pqsoOrig)
    {   pqsoOrig->pqsoTrNext = pqsoPrev;
	pqsoOrig->iFlags |= mTXTRC;
	pqsoTrTxLast = pqsoOrig;
    }
    if( pqsoPrev)
    {   pqsoPrev->pqsoTrNext = pqsoThis;
	pqsoPrev->iFlags |= mRXTRC;
	pqsoTrRxLast = pqsoPrev;
    }
    if( pqsoThis)
	pqsoThis->pqsoTrNext = 0;
}

NameTraceLoop( ptrc)
pTRC			ptrc;
{   pQSO		pqsoTx, pqsoRx, pqsoSkip;

    do
    {   NameCheckAndPrint( ptrc);
	pqsoSkip = 0;
	if( (pqsoTx = ptrc->pqsoThis) == 0)
	    pqsoRx = 0;
	else if( pqsoTx->pqsoTrPair)
	    pqsoRx = pqsoTx->pqsoTrPair;
	else if( pqsoTx->pqsoTxSkip)
	    pqsoRx = pqsoTx->pqsoTxSkip;
	else if( pqsoTx->pqsoMatch)
	    pqsoRx = pqsoTx->pqsoMatch;
	else if( pqsoTx->iFlags & mCLONE)
	    pqsoRx = 0;
	else if( pqsoTx->pcallRx->pqsoTxHead == 0)
	    pqsoRx = pqsoSkip = pqsoFindRxNameNext( pqsoTx);
	else if( (pqsoTx->pcallRx->iFlags & opMISSQ)
	&& pqsoFindTxNr( pqsoTx->pcallRx, pqsoTx->iRxNr) == 0)
	    pqsoRx = pqsoSkip = pqsoFindRxNameNext( pqsoTx);
	else
	    pqsoRx = 0;
	if( pqsoSkip)
	{   pqsoTx->pqsoTxSkip = pqsoRx;
	    pqsoRx->pqsoRxSkip = pqsoTx;
	}
	ptrc->pqsoOrig = pqsoTx;
	ptrc->pqsoPrev = pqsoRx;
	ptrc->pqsoThis = pqsoRx ? pqsoRx->pqsoTxNext : 0;
	ptrc->iOrigPrinted = ptrc->iThisPrinted;
	ptrc->iThisPrinted = 0;
    } while( pqsoTx || pqsoRx);
    printf( "\n");
}

NameTraceTx( pqso)
pQSO			pqso;
{   TRC			trc;

    trc.pqsoOrig = 0;
    trc.pqsoPrev = 0;
    trc.pqsoThis = pqsoTrHead = pqso;
    trc.iOrigPrinted = 0;
    trc.iThisPrinted = 0;
    NameTraceLoop( &trc);
}

NameTraceRx( pqso)
pQSO			pqso;
{   TRC			trc;

    trc.pqsoOrig = 0;
    trc.pqsoPrev = pqsoTrHead = pqso;
    trc.pqsoThis = pqso->pqsoTrNext = pqso->pqsoTxNext;
    trc.iOrigPrinted = 0;
    trc.iThisPrinted = 0;
    NameTraceLoop( &trc);
}

NameTraceDumpFwd()
{   pQSO		pqso;

    for( pqso = pqsoTrHead; pqso; pqso = pqso->pqsoTrNext)
	TR_PQ( "dump", pqso)
    printf( "\n");
}

NameTraceDumpBkwd( pqsoTx, pqsoRx)
pQSO			pqsoTx, pqsoRx;
{   pQSO		pqsoSkip;

    while( 1)
    {   if( (pqsoTrTxFirst = pqsoTx) != 0)
	{   TR_PQ( "tx", pqsoTx)
	    pqsoRx = pqsoTx->pqsoTxPrev;
	    pqsoTx = 0;
	}
	else if( pqsoRx == 0)
	    break;
	if( (pqsoTrRxFirst = pqsoRx) != 0)
	{   TR_PQ( "rx", pqsoRx)
	    pqsoSkip = 0;
	    if( pqsoRx->pqsoTrPair)
		pqsoTx = pqsoRx->pqsoTrPair;
	    else if( pqsoRx->pqsoRxSkip)
		pqsoTx = pqsoRx->pqsoRxSkip;
	    else if( pqsoRx->pqsoMatch)
		pqsoTx = pqsoRx->pqsoMatch;
	    else if( pqsoRx->pcallRx->pqsoTxHead == 0)
		pqsoTx = pqsoSkip = pqsoFindTxNamePrev( pqsoRx);
	    else if( pqsoRx->pcallRx->iFlags & opMISSQ)
		pqsoTx = pqsoSkip = pqsoFindTxNamePrev( pqsoRx);
	    else
		pqsoTx = 0;
	    if( pqsoSkip)
	    {   pqsoTx->pqsoTxSkip = pqsoRx;
		pqsoRx->pqsoRxSkip = pqsoTx;
	    }
	    pqsoRx = 0;
	}
	else if( pqsoTx == 0)
	    break;
    }
    printf( "\n");
}

#define nMAXARG		10

NameTraceCmdLoop()
{   char		acLine[ LINSIZ];
    char		acCmd[ LINSIZ];
    TEXT		atArg[ nMAXARG];
    int			nArg;
    pFUN		pfun;

    TrHelp();
    pfCmd = stdin;
    while( 1)
    {   if( pfCmd == stdin)
	{   fflush( stdout);
	    fprintf( stderr, "\nspr-tr> ");
	}
	if( fgets( acLine, LINSIZ, pfCmd) == 0)
	    if( pfCmd == stdin)
		return;
	    else
	    {   pfCmd = stdin;
		continue;
	    }
	strcpy( acCmd, acLine);
	nArg = nArgParse( acCmd, atArg, nMAXARG);
	if( nArg)
	{   for( pfun = afun; pfun < pfunEnd; pfun++)
		if( strcmp( pfun->tName, atArg[ 0]) == 0)
		    break;
	    if( pfun < pfunEnd && pfun->pfFun != TrHelp)
		printf( "\n%s", acLine);
	    if( pfun < pfunEnd)
		(*pfun->pfFun)( nArg - 1, atArg + 1);
	}
    }
}

int
nArgParse( t, atArg, nMaxArg)
TEXT			t, atArg[];
int			nMaxArg;
{   int			nArg;

    nArg = 0;
    while( nArg < nMaxArg - 1)
    {   while( *t && (*t == ' ' || *t == '\t' || *t == '\n'))
	    t++;
	if( *t == 0)
	    break;
	atArg[ nArg++] = t;
	while( *t && ! (*t == ' ' || *t == '\t' || *t == '\n'))
	    t++;
	if( *t == 0)
	    break;
	*t++ = 0;
    }
    atArg[ nArg] = 0;
    return nArg;
}

TrHelp()
{   pFUN		pfun;

    fprintf( stderr, "\ninteractive trace cmds:\n");
    for( pfun = afun; pfun < pfunEnd; pfun++)
	fprintf( stderr, "%s\t%s\n", pfun->tName, pfun->tExplain);
}

TrCmt()
{
}

TrTxFwd( nArg, atArg)
int			nArg;
TEXT			atArg[];
{   pCALL		pcall;
    pQSO		pqso;

    if( nArg != 2)
	return;
    pcall = pcallHash( atArg[ 0]);
    pqso = pqsoFindTxNr( pcall, atoi( atArg[ 1]));
    if( pqso == 0)
	return;
    NameTraceTx( pqso);
}

TrRxFwd( nArg, atArg)
int			nArg;
TEXT			atArg[];
{   pCALL		pcallRx;
    int			iRxNr;
    pQSO		pqso;

    if( nArg < 2 || nArg > 3)
	return;
    pcallRx = pcallHash( atArg[ 0]);
    iRxNr = atoi( atArg[ 1]);
    if( nArg == 2)
	pqso = pqsoFindCheckRxNr( pcallRx, iRxNr);
    if( nArg == 3)
	pqso = pqsoFindRxNrCall( pcallRx, iRxNr, pcallHash( atArg[ 2]));
    if( pqso == 0)
	return;
    NameTraceRx( pqso);
}

TrTxBak( nArg, atArg)
int			nArg;
TEXT			atArg[];
{   pCALL		pcall;
    pQSO		pqso;

    if( nArg != 2)
	return;
    pcall = pcallHash( atArg[ 0]);
    pqso = pqsoFindTxNr( pcall, atoi( atArg[ 1]));
    if( pqso == 0)
	return;
    NameTraceDumpBkwd( pqso, 0);
}

TrRxBak( nArg, atArg)
int			nArg;
TEXT			atArg[];
{   pCALL		pcallRx;
    int			iRxNr;
    pQSO		pqso;

    if( nArg < 2 || nArg > 3)
	return;
    pcallRx = pcallHash( atArg[ 0]);
    iRxNr = atoi( atArg[ 1]);
    if( nArg == 2)
	pqso = pqsoFindCheckRxNr( pcallRx, iRxNr);
    if( nArg == 3)
	pqso = pqsoFindRxNrCall( pcallRx, iRxNr, pcallHash( atArg[ 2]));
    if( pqso == 0)
	return;
    NameTraceDumpBkwd( 0, pqso);
}

TrReTr( nArg, atArg)
int			nArg;
TEXT			atArg[];
{   pQSO		pqso;

    if( nArg != 0)
	return;
    if( pqsoTrRxFirst)
	NameTraceRx( pqsoTrRxFirst);
    else if( pqsoTrTxFirst)
	NameTraceTx( pqsoTrTxFirst);
}

TrReView( nArg, atArg)
int			nArg;
TEXT			atArg[];
{   
    if( nArg == 0)
    {   if( pqsoTrRxLast)
	    TR_PQ( "rx", pqsoTrRxLast)
	if( pqsoTrTxLast)
	    TR_PQ( "tx", pqsoTrTxLast)
    }
    else
	NameTraceDumpFwd();
}

TrPrCall( nArg, atArg)
int			nArg;
TEXT			atArg[];
{   pCALL		pcall;
    pQSO		pqso;

    if( nArg != 1)
	return;
    pcall = pcallHash( atArg[ 0]);
    printf( "call=%s qth=%s tx%s rx%s tadj=%d nref=%d flg=0x%04X\n",
	pcall->pstrCall->ac,
	pcall->pstrQth ? pcall->pstrQth->ac : "",
	pcall->pqsoTxHead ? "y" : "n",
	pcall->pqsoRxHead ? "y" : "n",
	pcall->iTimeAdj,
	pcall->nRef,
	pcall->iFlags);
}

TrPrQso( nArg, atArg)
int			nArg;
TEXT			atArg[];
{   pCALL		pcall;
    int			iNr;
    pQSO		pqso;

    if( nArg == 0)
	TrPrQsoStatic();
    else if( nArg == 2 || nArg == 3)
    {   pcall = pcallHash( atArg[ 0]);
	iNr = atoi( atArg[ 1]);
	if( nArg == 2)
	{   pqso = pqsoFindTxNr( pcall, iNr);
	    if( pqso == 0)
		pqso = pqsoFindRxNr( pcall, iNr);
	}
	else if( nArg == 3)
	{   pqso = pqsoFindTxNrCall( pcall, iNr, pcallHash( atArg[ 2]));
	    if( pqso == 0)
		pqso = pqsoFindRxNrCall( pcall, iNr, pcallHash( atArg[ 2]));
	}
	if( pqso)
	    TrPrQsoAndLinks( pqso);
    }
}

TrPrQsoStatic()
{
    TR_PQ( "head", pqsoTrHead)
    TR_PQ( "txfr", pqsoTrTxFirst)
    TR_PQ( "rxfr", pqsoTrRxFirst)
    TR_PQ( "txls", pqsoTrTxLast)
    TR_PQ( "rxls", pqsoTrRxLast)
}

TrPrQsoAndLinks( pqso)
pQSO			pqso;
{
    TR_PQ( "prev", pqso->pqsoTxPrev)
    TR_PQ( "this", pqso)
    TR_PQ( "next", pqso->pqsoTxNext)
    if( pqso->pqsoMatch == 0 && pqso->pqsoTrPair == 0 &&
	pqso->pqsoTxSkip == 0 && pqso->pqsoRxSkip == 0)
	    return;
    printf( "\n");
    TR_PQ( "rskp", pqso->pqsoRxSkip)
    TR_PQ( "this", pqso)
    TR_PQ( "tskp", pqso->pqsoTxSkip)
    if( pqso->pqsoMatch && (pqso->pqsoMatch != pqso->pqsoTrPair))
	TR_PQ( "mtch", pqso->pqsoMatch)
    if( pqso->pqsoTrPair && (pqso->pqsoMatch != pqso->pqsoTrPair))
	TR_PQ( "pair", pqso->pqsoTrPair)
    if( pqso->pqsoMatch && (pqso->pqsoMatch == pqso->pqsoTrPair))
	TR_PQ( "mtpr", pqso->pqsoTrPair)
}

TrDel( nArg, atArg)
int			nArg;
TEXT			atArg[];
{   pCALL		pcall;
    pQSO		pqso;

    if( nArg != 2)
	return;
    pcall = pcallHash( atArg[ 0]);
    pqso = pqsoFindTxNr( pcall, atoi( atArg[ 1]));
    if( pqso == 0)
	return;
    if( pqso->pqsoTxNext)
	pqso->pqsoTxNext->pqsoTxPrev = pqso->pqsoTxPrev;
    if( pqso->pqsoTxPrev)
	pqso->pqsoTxPrev->pqsoTxNext = pqso->pqsoTxNext;
    else
	pqso->pcallTx->pqsoTxHead = pqso->pqsoTxNext;
}

TrPair( nArg, atArg)
int			nArg;
TEXT			atArg[];
{   pCALL		pcallTx, pcallRx;
    pQSO		pqsoTx, pqsoRx;

    if( nArg != 4)
	return;
    pcallTx = pcallHash( atArg[ 0]);
    pcallRx = pcallHash( atArg[ 2]);
    pqsoTx = pqsoFindTxNr( pcallTx, atoi( atArg[ 1]));
    pqsoRx = pqsoFindTxNr( pcallRx, atoi( atArg[ 3]));
    if( pqsoTx == 0 || pqsoRx == 0)
	return;
    pqsoTx->pqsoTrPair = pqsoRx;
    pqsoRx->pqsoTrPair = pqsoTx;
    pqsoTx->pqsoTxSkip = pqsoTx->pqsoRxSkip = 0;
    pqsoRx->pqsoTxSkip = pqsoRx->pqsoRxSkip = 0;
}

TrSkip( nArg, atArg)
int			nArg;
TEXT			atArg[];
{   pCALL		pcallTx, pcallRx;
    pQSO		pqsoTx, pqsoRx;

    if( nArg < 4 || nArg > 5)
	return;
    pcallTx = pcallHash( atArg[ 0]);
    pcallRx = pcallHash( atArg[ 2]);
    pqsoTx = pqsoFindTxNr( pcallTx, atoi( atArg[ 1]));
    pqsoRx = pqsoFindTxNr( pcallRx, atoi( atArg[ 3]));
    if( pqsoTx == 0 || pqsoRx == 0)
	return;
    pqsoTx->pqsoTxSkip = pqsoRx;
    pqsoRx->pqsoRxSkip = pqsoTx;
    pqsoTx->pqsoTrPair = pqsoRx->pqsoTrPair = 0;
    if( nArg == 5)
	pqsoTx->pstrTxJump = pstrHash( atArg[ 4]);
}

TrClone( nArg, atArg)
int			nArg;
TEXT			atArg[];
{   pCALL		pcall;
    pQSO		pqso;

    if( nArg != 2)
	return;
    pcall = pcallHash( atArg[ 0]);
    pqso = pqsoFindTxNr( pcall, atoi( atArg[ 1]));
    if( pqso == 0)
	return;
    pqso->iFlags |= mCLONE;
}

TrClear( nArg, atArg)
int			nArg;
TEXT			atArg[];
{   pCALL		pcall;
    pQSO		pqso;

    for( pcall = pcallHead; pcall; pcall = pcall->pcallNext)
	for( pqso = pcall->pqsoTxHead; pqso; pqso = pqso->pqsoTxNext)
	{   pqso->pqsoTrPair = 0;
	    pqso->pqsoTxSkip = 0;
	    pqso->pqsoRxSkip = 0;
	}
}

TrClob( nArg, atArg)
int			nArg;
TEXT			atArg[];
{   pCALL		pcall;
    pQSO		pqso, pqsoHead;

    for( pcall = pcallHead; pcall; pcall = pcall->pcallNext)
	for( pqso = pqsoHead = pcall->pqsoTxHead; pqso; pqso = pqso->pqsoTxNext)
	    pqso->iFlags &= ~ (mRXTRC | mTXTRC);
}

TrAll( nArg, atArg)
int			nArg;
TEXT			atArg[];
{   pCALL		pcall;
    pQSO		pqso;

    for( pcall = pcallHead; pcall; pcall = pcall->pcallNext)
	if( pcall->pqsoTxHead)
	    NameTraceTx( pcall->pqsoTxHead);
	else
	    for( pqso = pcall->pqsoRxHead; pqso; pqso = pqso->pqsoRxNext)
		if( pqso->iRxNr == 1 && pqso->pqsoRxSkip == 0)
		    NameTraceRx( pqso);
}

TrNext( nArg, atArg)
int			nArg;
TEXT			atArg[];
{   static pQSO		pqsoStart, pqso;
    pSTR		pstrName;
    pCALL		pcall;

    pstrName = nArg == 1 ? pstrHash( atArg[ 0]) : 0;
    if( nArg == 2)
    {   pcall = pcallHash( atArg[ 0]);
	pqsoStart = pqsoFindTxNr( pcall, atoi( atArg[ 1]));
    }
    if( pqsoStart == 0)
	pqsoStart = pqsoFirst;
    pqso = pqsoStart;
    while( 1)
    {   if( (pqso->iFlags & mRXTRC) == 0)
	    if( pstrName == 0 || pstrName == pqso->pstrRxName)
		break;
	if( (pqso->iFlags & mTXTRC) == 0)
	    if( pstrName == 0 || pstrName == pqso->pstrTxName)
		break;
	if( pqso->pqsoTxNext)
	    pqso = pqso->pqsoTxNext;
	else
	{   pcall = pqso->pcallTx;
	    do
		if( pcall->pcallNext)
		    pcall = pcall->pcallNext;
		else
		    pcall = pcallHead;
	    while( (pqso = pcall->pqsoTxHead) == 0);
	}
	if( pqso == pqsoStart)
	    break;
    }
    if( pqso == pqsoStart)
	TR_PQ( "ng", pqso)
    else
	TrPrQsoAndLinks( pqso);
}

TrPct( nArg, atArg)
int			nArg;
TEXT			atArg[];
{   pCALL		pcall, pcallArg;
    pQSO		pqso;
    int			nTxBy, nRxBy, nBy;
    int			nTxTo, nRxFm, nWith;

    nTxBy = nRxBy = nBy = 0;
    nTxTo = nRxFm = nWith = 0;
    pcallArg = nArg == 1 ? pcallHash( atArg[ 0]) : 0;
    for( pcall = pcallHead; pcall; pcall = pcall->pcallNext)
	if( pcall == pcallArg || pcallArg == 0)
	{   for( pqso = pcall->pqsoTxHead; pqso; pqso = pqso->pqsoTxNext)
	    {   if( pqso->iFlags & mTXTRC)
		    nTxBy++;
		if( pqso->iFlags & mRXTRC)
		    nRxBy++;
		nBy++;
	    }
	    for( pqso = pcall->pqsoRxHead; pqso; pqso = pqso->pqsoRxNext)
	    {   if( pqso->iFlags & mTXTRC)
		    nTxTo++;
		if( pqso->iFlags & mRXTRC)
		    nRxFm++;
		nWith++;
	    }
	}
    if( nBy)
	printf( "txby=%d/%d=%.1f%% rxby=%d/%d=%.1f%%\n",
	    nTxBy, nBy, (100.0 * nTxBy) / nBy,
	    nRxBy, nBy, (100.0 * nRxBy) / nBy);
    if( nWith)
	printf( "txto=%d/%d=%.1f%% rxfm=%d/%d=%.1f%%\n",
	    nTxTo, nWith, (100.0 * nTxTo) / nWith,
	    nRxFm, nWith, (100.0 * nRxFm) / nWith);
}

TrLog( nArg, atArg)
int			nArg;
TEXT			atArg[];
{   pCALL		pcall;
    pQSO		pqso;
    int			iTime;

    if( nArg < 1 || nArg > 2)
	return;
    pcall = pcallHash( atArg[ 0]);
    iTime = nArg >= 2 ? iHrMinToTime( atoi( atArg[ 1])) : iTIMEUNK;
    if( pcall->pqsoTxHead)
    {   for( pqso = pcall->pqsoTxHead; pqso; pqso = pqso->pqsoTxNext)
	    if( iTime == iTIMEUNK || pqso->iTime == iTIMEUNK ||
		iADJTIME( pqso) >= iTime - 5 && iADJTIME( pqso) <= iTime + 5)
		    TR_PQ( "log", pqso)
    }
    else
    {   for( pqso = pcall->pqsoRxHead; pqso; pqso = pqso->pqsoRxNext)
	    if( iTime == iTIMEUNK || pqso->iTime == iTIMEUNK ||
		iADJTIME( pqso) >= iTime - 5 && iADJTIME( pqso) <= iTime + 5)
		    TR_PQ( "log", pqso)
    }
}

TrName( nArg, atArg)
int			nArg;
TEXT			atArg[];
{   pSTR		pstrName;
    pCALL		pcall, pcallArg;
    pQSO		pqso;
    int			iTime;

    if( ! (nArg == 2 || nArg == 3))
	return;
    pstrName = pstrHash( atArg[ 0]);
    iTime = iHrMinToTime( atoi( atArg[ 1]));
    pcallArg = nArg == 3 ? pcallHash( atArg[ 2]) : 0;
    for( pcall = pcallHead; pcall; pcall = pcall->pcallNext)
	for( pqso = pcall->pqsoTxHead; pqso; pqso = pqso->pqsoTxNext)
	    if( pcallArg == 0 || pqso->pcallTx == pcallArg || pqso->pcallRx == pcallArg)
		if( pqso->pstrTxName == pstrName || pqso->pstrRxName == pstrName)
		    if( pqso->iTime == iTIMEUNK ||
			iADJTIME( pqso) >= iTime - 5 && iADJTIME( pqso) <= iTime + 5)
			    TR_PQ( "name", pqso)
}

TrTime( nArg, atArg)
int			nArg;
TEXT			atArg[];
{
    if( nArg == 1)
	iTrTime = atoi( atArg[ 0]);
}

TrSource( nArg, atArg)
int			nArg;
TEXT			atArg[];
{
    if( nArg != 1)
	return;
    pfCmd = fopen( atArg[ 0], "r");
    if( pfCmd == 0)
	pfCmd = stdin;
}

FUN	afun[] =
	{   TrHelp,	"?",		"print this message",
	    TrCmt,	"#",		"echoable comment",
	    TrTxFwd,	"txby",		"tx by <call> qso <nr>",
	    TrRxFwd,	"rxfm",		"rx fm <call> qso <nr> with [call]",
	    TrReView,	"rv",		"review trace [long]",
	    TrTxBak,	"bkby",		"bkwd tx by <call> <nr>",
	    TrRxBak,	"bkfm",		"bkwd rx fm <call> <nr> with [call]",
	    TrReTr,	"rt",		"retrace from bkwd trace",
	    TrPrCall,	"pc",		"print info <call>",
	    TrPrQso,	"pq",		"print qso <call> <nr> with [call]",
	    TrDel,	"del",		"delete qso <call> <nr>",
	    TrPair,	"pair",		"pair <call> <nr> with <call> <nr>",
	    TrSkip,	"skip",		"skip <call> <nr> over to <call> <nr> [jump]",
	    TrClone,	"clone",	"dcl <call> <nr> qso with no match",
	    TrNext,	"next",		"next un-traced qso [name] [call nr]",
	    TrClob,	"clob",		"clobber all trace flags",
	    TrClear,	"clr",		"clear all trace links",
	    TrAll,	"all",		"trace all starting names",
	    TrPct,	"pct",		"percent traced [call]",
	    TrLog,	"log",		"print log for <call> around [time]",
	    TrName,	"name",		"print <name> around <time> with [call]",
	    TrTime,	"time",		"<value> sets trace-time switch",
	    TrSource,	"so",		"source cmds from <file>",
	};

pFUN	pfunEnd = afun + sizeof( afun) / sizeof( FUN);


