
static char acRcs[] =
    "$Header: spf.c,v 5.2 95/04/11 22:54:56 k2mm Exp $";

#include <stdio.h>

#define nMAXFLD		20	/* max # of fields in FLD format */
#define nMAXFMT		20	/* max # of FLD formats */
#define nMAXSEC		500	/* max # of section names & aliases */
#define SECSIZ		20	/* section-name length */
#define LINSIZ		500	/* line length */
#define ISWHITE( c)	((c)== 0 || (c)==' ' || (c)=='\t' || (c)=='\n')

typedef char*		TEXT;
typedef TEXT*		pTEXT;
typedef struct col *	pCOL;
typedef struct col
{   char		cKey;
    TEXT		tDst;
    TEXT		tSrc;
    int			iLen;
}   COL;
typedef struct fld *	pFLD;
typedef struct fld
{   int			nField;
    pCOL		apcol[ nMAXFLD];
}   FLD;
typedef struct band *	pBAND;
typedef struct band
{   float		fMin;
    float		fMax;
    int			iWave;
}   BAND;
typedef struct sec *	pSEC;
typedef struct sec
{   char		acSect[ SECSIZ];
    pSEC		psecAlias;
}   SEC;

int			nLine;
int			iCheck;
int			iSect;
int			iNoOut;
int			iInLog;
int			iExpectFmt;
int			iColFmt;
int			nFldFmt;
int			nQso;
int			nSec;
char			acLine[ LINSIZ];
char			aacParse[ nMAXFLD][ LINSIZ];

SEC			asec[ nMAXSEC];
char			acDay[ LINSIZ];
char			acHour[ LINSIZ];
char			acMin[ LINSIZ];
char			acBand[ LINSIZ];
char			acTxCall[ LINSIZ];
char			acTxNmbr[ LINSIZ];
char			acTxName[ LINSIZ];
char			acTxSect[ LINSIZ];
char			acRxCall[ LINSIZ];
char			acRxNmbr[ LINSIZ];
char			acRxName[ LINSIZ];
char			acRxSect[ LINSIZ];
int			iHour;
int			iMin;
int			iBand;
int			iTxNmbr;
int			iRxNmbr;

FLD			afld[ nMAXFMT];
COL			acol[] =
			{   { 'd', acDay },    /* GMT Day */
			    { 'y', acHour },   /* GMT Hour */
			    { 'z', acMin },    /* GMT Minute */
			    { 'b', acBand },   /* Band */
			    { 'm', acTxNmbr }, /* My-number */
			    { 'o', acTxCall }, /* Originator */
			    { 't', acTxName }, /* This-name */
			    { 'q', acTxSect }, /* my-Qth */
			    { 'n', acRxNmbr }, /* rx-Number */
			    { 'c', acRxCall }, /* rx-Call */
			    { 'h', acRxName }, /* rx-Handle */
			    { 's', acRxSect }, /* rx-Section */
			};
int			nColFmt = sizeof( acol) / sizeof( COL);
BAND			aband[] =
			{   { 1.8,     2, 160 },
			    { 3.5,     4,  80 },
			    {   7,   7.3,  40 },
			    {  14, 14.35,  20 },
			    {  21, 21.45,  15 },
			    {  28,  29.7,  10 },
			    { 160,   160, 160 },
			    {  75,    80,  80 },
			    {  40,    40,  40 },
			    {  20,    20,  20 },
			    {  15,    15,  15 },
			    {  10,    10,  10 },
			};
int			nBand = sizeof( aband) / sizeof( BAND);

extern pSEC		psecFind();
extern pSEC		psecNew();

main( nArg, atArg)
int			nArg;
TEXT			atArg[];
{
    InitArgs( nArg - 1, atArg + 1);
    FormatLogs();
}

#define IFARG( x)	if( strcmp( *ptArg, x) == 0)

InitArgs( nArg, ptArg)
int			nArg;
pTEXT			ptArg;
{
    ptArg[ nArg] = 0;
    while( *ptArg)
    {   IFARG( "-c")
	    iCheck = 1;
	else IFARG( "-s")
	    iSect = 1;
	else IFARG( "-n")
	    iNoOut = 1;
	ptArg++;
    }
}

FormatLogs()
{   char		acStdin[ LINSIZ];

    while( fgets( acStdin, LINSIZ, stdin))
    {   nLine++;
	if( acStdin[ 0] == '#')
	    continue;
	ExpandTabs( acStdin, acLine, LINSIZ);
	if( acLine[ 0] == '%')
	    SetCmd();
	else if( iExpectFmt)
	{   SetFmt();
	    iExpectFmt = 0;
	}
	else if( iInLog)
	    SetQso();
	if( iCheck)
	{   printf( "%s\n", acLine);
	    fflush( stdout);
	}
	else if( acRxCall[ 0])
	{   if( iSect)
	    {   if( psecFind( acRxSect) == 0)
		    psecNew( 0, acRxSect);
	    }
	    else if( ! iNoOut)
	    {   PrintQso();
		if( acRxName[ 0] && acRxName[ 0] != '-')
		    strcpy( acTxName, acRxName);
	    }
	    acHour[ 0] = acMin[ 0] = 0;
	    acTxNmbr[ 0] = acRxNmbr[ 0] = 0;
	    acRxCall[ 0] = acRxName[ 0] = acRxSect[ 0] = 0;
	}
    }
    if( iSect || iNoOut)
	PrintSect();
}

ExpandTabs( tSrc, tDst, iDstSize)
TEXT			tSrc, tDst;
int			iDstSize;
{   TEXT		tEnd;
    int			n, i;

    tEnd = tDst + iDstSize - 1;
    n = 0;
    while( 1)
	if( (i = *tSrc++ & 0x7F) == 0 || tDst >= tEnd)
	{   if( tDst <= tEnd)
		*tDst = 0;
	    return;
	}
	else if( i == '\t')
	    do
	    {   *tDst++ = ' ';
		n++;
	    } while( (n & 7) && tDst < tEnd);
	else if( i >= 'A' && i <= 'Z')
	{   *tDst++ = i + 'a' - 'A';
	    n++;
	}
	else if( i >= ' ' && i <= '~')
	{   *tDst++ = i;
	    n++;
	}
}

#define IFCMD( x)	if( strncmp( acLine, x, sizeof( x) - 1) == 0)

SetCmd()
{
    IFCMD( "%sec")
	SetSec();
    else IFCMD( "%log")
	SetLog();
    else IFCMD( "%col")
	iExpectFmt = 1;
    else IFCMD( "%fld")
	SetFld();
    else IFCMD( "%set")
	SetSet();
    else IFCMD( "%end")
	iInLog = 0;
    else IFCMD( "%spr")
	printf( "%%%s\n", acLine + 5);
}

SetQso()
{   int			iFld;
    char		acErr[ LINSIZ];
    char		acMsg[ LINSIZ];

    acErr[ 0] = 0;
    if( iColFmt)
	DecodeCol();
    if( nFldFmt)
	DecodeFld();
    if( acRxCall[ 0])
    {   nQso++;
	if( acHour[ 0])
	    if( sscanf( acHour, "%d", &iHour) == 0)
		iHour = 99;
	if( acMin[ 0])
	    if( sscanf( acMin, "%d", &iMin) == 0)
		iMin = 99;
	if( acBand[ 0])
	    DecodeBand();
	if( acTxNmbr[ 0])
	    if( sscanf( acTxNmbr, "%d", &iTxNmbr) != 1)
		Warn( "bad tx-number fmt");
	    else if( iTxNmbr != nQso)
	    {   Warn( "out-of-sequence tx-number");
		nQso = iTxNmbr;
	    }
	iTxNmbr = nQso;
	if( sscanf( acRxNmbr, "%d", &iRxNmbr) != 1)
	{   strcat( acErr, " rx-number");
	    iRxNmbr = 0;
	}
	if( acRxName[ 0] == 0)
	{   strcat( acErr, " rx-name");
	    strcpy( acRxName, "-");
	}
	if( acRxSect[ 0] == 0)
	{   strcat( acErr, " rx-sec");
	    strcpy( acRxSect, "-");
	}
	if( acErr[ 0])
	{   sprintf( acMsg, "busted/bad-fmt:%s", acErr);
	    Warn( acMsg);
	}
    }
}

DecodeCol()
{   TEXT		tEnd;
    int			iFmt, n;
    pCOL		pcol;
    char		acVal[ LINSIZ];
    char		acExt[ LINSIZ];

    tEnd = acLine + strlen( acLine);
    for( iFmt = 0, pcol = acol; iFmt < nColFmt; iFmt++, pcol++)
    {   if( pcol->tSrc == 0)
	    continue;
	pcol->tDst[ 0] = 0;
	if( pcol->tSrc > tEnd)
	    continue;
	strncpy( acVal, pcol->tSrc, pcol->iLen);
	acVal[ pcol->iLen] = 0;
	n = sscanf( acVal, "%s %s", pcol->tDst, acExt);
	if( n > 1)
	{   sprintf( acExt, "%c-field overrun", pcol->cKey);
	    Warn( acExt);
	}
    }
}

int
nParse()
{   TEXT		tSrc, tDst;
    int			nField, iGot;

    tSrc = acLine;
    nField = iGot = 0;
    while( nField < nMAXFLD)
    {   if( ! ISWHITE( *tSrc))
	{   if( iGot == 0)
	    {   tDst = aacParse[ nField++];
		iGot = 1;
	    }
	    *tDst++ = *tSrc;
	}
	else if( iGot)
	    *tDst = iGot = 0;
	if( *tSrc++ == 0)
	    return nField;
    }
}

DecodeFld()
{   int			nField, iField, iFmt, iCol;
    pFLD		pfld;
    pCOL		pcol;

    nField = nParse();
    for( iFmt = 0, pfld = afld; iFmt < nFldFmt; iFmt++, pfld++)
	if( pfld->nField == nField)
	    for( iField = 0; iField < nField; iField++)
	    {   pcol = pfld->apcol[ iField];
		if( pcol && (pcol->tSrc == 0 || pcol->tDst[ 0] == 0))
		    strcpy( pcol->tDst, aacParse[ iField]);
	    }
}

DecodeBand()
{   float		fBand;
    pBAND		pband;

    if( sscanf( acBand, "%f", &fBand) != 1)
	Warn( "bad band fmt");
    for( iBand = 0, pband = aband; iBand < nBand; iBand++, pband++)
	if( fBand >= pband->fMin && fBand <= pband->fMax)
	    return;
    Warn( "out-of-band");
}

SetSec()
{   int			nField, iField;
    TEXT		tSect;
    pSEC		psec, psecMain;

    nField = nParse();
    for( iField = 1; iField < nField; iField++)
    {   tSect = aacParse[ iField];
	psec = psecFind( tSect);
	if( iField == 1)
	    psecMain = psec ? psec : psecNew( 0, tSect);
	else if( psec == 0)
	    psecNew( psecMain, tSect);
    }
}

SetLog()
{
    iInLog = 1;
    for( iColFmt = 0; iColFmt < nColFmt; iColFmt++)
	acol[ iColFmt].tSrc = 0;
    iColFmt = 0;
    nFldFmt = 0;
    nQso = 0;
    iHour = iMin = 99;
    acBand[ 0] = 0;
    if( sscanf( acLine, "%*s %s %s %s", acTxCall, acTxName, acTxSect) != 3)
	Warn( "bad log cmd");
    if( iSect && psecFind( acTxSect) == 0)
	psecNew( 0, acTxSect);
}

SetFmt()
{   int			iFmtNew, iFmtOld;
    TEXT		t;

    iColFmt = 1;
    iFmtOld = nColFmt;
    t = acLine;
    do
    {   for( iFmtNew = 0; iFmtNew < nColFmt; iFmtNew++)
	    if( acol[ iFmtNew].cKey == *t)
		break;
	if( iFmtNew == nColFmt && *t && ! ISWHITE( *t))
	    Warn( "bad col fmt");
	if( iFmtNew == iFmtOld)
	    continue;
	if( iFmtOld != nColFmt)
	    acol[ iFmtOld].iLen = t - acol[ iFmtOld].tSrc;
	if( iFmtNew != nColFmt)
	    acol[ iFmtNew].tSrc = t;
	iFmtOld = iFmtNew;
    } while( *t++);
}

SetFld()
{   pFLD		pfld;
    int			iField, iCol;
    char		cKey;

    if( nFldFmt == nMAXFMT)
    {   Warn( "max # %fld fmts exceeded");
	return;
    }
    pfld = afld + nFldFmt++;
    pfld->nField = nParse() - 1;
    for( iField = 0; iField < pfld->nField; iField++)
    {   cKey = aacParse[ iField + 1][ 0];
	for( iCol = 0; iCol < nColFmt; iCol++)
	    if( acol[ iCol].cKey == cKey)
		break;
	if( iCol != nColFmt)
	    pfld->apcol[ iField] = acol + iCol;
	else
	{   pfld->apcol[ iField] = 0;
	    if( cKey != 'x')
		Warn( "bad fld fmt");
	}
    }
}

SetSet()
{   char		acKey[ LINSIZ], acVal[ LINSIZ];
    int			iCol;

    acKey[ 0] = acVal[ 0] = 0;
    sscanf( acLine + 4, "%s %s", acKey, acVal);
    for( iCol = 0; iCol < nColFmt; iCol++)
	if( acol[ iCol].cKey == acKey[ 0])
		break;
    if( iCol == nColFmt || acKey[ 1] != 0 || acVal[ 0] == 0)
	Warn( "bad set fmt");
    else
	strcpy( acol[ iCol].tDst, acVal);
}

pSEC
psecFind( tSect)
TEXT			tSect;
{   int			iSec;
    pSEC		psec;

    for( iSec = 0, psec = asec; iSec < nSec; iSec++, psec++)
	if( strcmp( psec->acSect, tSect) == 0)
	    return psec;
    return 0;
}

pSEC
psecNew( psecAlias, tSect)
pSEC			psecAlias;
TEXT			tSect;
{   pSEC		psec;

    if( nSec >= nMAXSEC)
    {   Warn( "max # sections/aliases exceeded");
	return 0;
    }
    psec = asec + nSec++;
    strncpy( psec->acSect, tSect, SECSIZ - 1);
    psec->acSect[ SECSIZ - 1] = 0;
    if( psecAlias)
	psec->psecAlias = psecAlias;
    else
	psec->psecAlias = psec;
    return psec;
}

ClobberPortable( t)
TEXT			t;
{
    while( *t)
    {   if( *t == '/' && (t[ 1] == 0 || t[ 2] == 0 || t[ 3] == 0))
	{   *t = 0;
	    return;
	}
	t++;
    }
}

PrintQso()
{   pSEC		psecTx, psecRx;

    ClobberPortable( acTxCall);
    ClobberPortable( acRxCall);
    psecTx = psecFind( acTxSect);
    psecRx = psecFind( acRxSect);
    if( psecTx == 0 || psecRx == 0)
	Warn( "unrecognized section");
    printf( " %3d", aband[ iBand].iWave);
    printf( " %02d%02d", iHour, iMin);
    printf( " %4d %-10s %-14s %-4s", iTxNmbr, acTxCall, acTxName,
	psecTx ? psecTx->psecAlias->acSect : "-");
    printf( " %4d %-10s %-14s %-4s", iRxNmbr, acRxCall, acRxName,
	psecRx ? psecRx->psecAlias->acSect : "-");
    printf( "\n");
    fflush( stdout);
}

PrintSect()
{   int			iSecPrim, iSecAlias;
    pSEC		psecPrim, psecAlias;

    for( iSecPrim = 0, psecPrim = asec; iSecPrim < nSec; iSecPrim++, psecPrim++)
    if( psecPrim->psecAlias == psecPrim)
    {   printf( "%%sec %s", psecPrim->acSect);
	for( iSecAlias = 0, psecAlias = asec; iSecAlias < nSec; iSecAlias++, psecAlias++)
	    if( psecAlias != psecPrim && psecAlias->psecAlias == psecPrim)
		printf( " %s", psecAlias->acSect);
	printf( "\n");
    }
}

Warn( tMsg)
TEXT			tMsg;
{   static int		nWarn;

    nWarn++;
    fprintf( stderr, "# %s, line %d\n", tMsg, nLine);
    fflush( stderr);
    if( iSect || iNoOut)
	return;
    printf( "# %s\n", tMsg);
    fflush( stdout);
}


