/******************************************************************************
* Module    :   Parse --- Search for a particular line.
*
* Author    :   John W. M. Stevens
******************************************************************************/

#include    "compiler.h"

#include    "unpost.h"
#include    "regexp.h"
#include    "uudec.h"
#include    "modflnm.h"
#include    "ident.h"
#include    "parse.h"
#include    "config.h"
#include    "utils.h"

/*  These are the elements we have to parse out of either the header or
*   the body of the message BEFORE we find the first UUencoded line.
*/
typedef enum    {
    ID_STRING,
    SEGMENT_NO,
    NO_SEGMENTS
} PARSE_ELS;

/*
*   Regular expression source strings.
*
*   To configure the program for different systems, these are the
*   strings to change.
*/
static  PART_RE Parts1[] =
{
    {   "^Subject:(.*)[[({]Part[_ \t]*([0-9]+)[^0-9]+([0-9]+)[)\\]}](.*)",
        1,  2,  3,  4,  IGN_CASE,   NULL
    },
    {   "^Subject:(.*)Part[_ \t]*[[({]([0-9]+)[^0-9]+([0-9]+)[)\\]}](.*)",
        1,  2,  3,  4,  IGN_CASE,   NULL
    },
    {   "^Subject:(.*)Part[_ \t]+([0-9]+)[^0-9]+([0-9]+)(.*)",
        1,  2,  3,  4,  IGN_CASE,   NULL
    },
    {   "^Subject:(.*)[([{][ \t]*([0-9]+)[^0-9]+([0-9]+)[ \t]*[)\\]}](.*)",
        1,  2,  3,  4,  IGN_CASE,   NULL
    },
    {   "^Subject:(.*)([0-9]+)([/|]|[ \t]+of[ \t]+)([0-9]+)(.*)",
        1,  2,  4,  5,  IGN_CASE,   NULL
    },
    {   "^Subject:(.*)",
        1,  0,  0,  0,  IGN_CASE,   NULL
    },
    {   NULL,
        0,  0,  0,  0,  IGN_CASE,   NULL
    }
};

static  PART_RE Parts3[] =
{
    {   "^X-File-Name:[ \t]+(.*)",
        1,  0,  0,  0,  CASE_SENSITIVE,     NULL
    },
    {   NULL,
        0,  0,  0,  0,  IGN_CASE,   NULL
    }
};

static  PART_RE Parts4[] =
{
    {   "^X-Part:[ \t]+([0-9]+)",
        0,  1,  0,  0,  CASE_SENSITIVE,     NULL
    },
    {   NULL,
        0,  0,  0,  0,  IGN_CASE,   NULL
    }
};

static  PART_RE Parts5[] =
{
    {   "^X-Part-Total:[ \t]+([0-9]+)",
        0,  0,  1,  0,  CASE_SENSITIVE,     NULL
    },
    {   NULL,
        0,  0,  0,  0,  IGN_CASE,   NULL
    }
};

static  PART_RE Parts6[] =
{
    {   "^Uusplit-part:[ \t]+([0-9]+)",
        0,  1,  0,  0,  CASE_SENSITIVE,     NULL
    },
    {   NULL,
        0,  0,  0,  0,  IGN_CASE,   NULL
    }
};

static  PART_RE Parts7[] =
{
    {   "^Uusplit-parts:[ \t]+([0-9]+)",
        0,  0,  1,  0,  CASE_SENSITIVE,     NULL
    },
    {   NULL,
        0,  0,  0,  0,  IGN_CASE,   NULL
    }
};

static  PART_RE Parts8[] =
{
    {   "^section ([0-9]+) of uuencode [0-9]+\\.[0-9]+ of file ([^ \t]+)[ \t]+by R.E.M.",
        2,  1,  0,  0,  CASE_SENSITIVE,     NULL
    },
    {   NULL,
        0,  0,  0,  0,  IGN_CASE,   NULL
    }
};

/*
static  PART_RE Parts9[] =
{
    {   "^([^ \t]+)[ \t]+section[ \t]+([0-9]+)/([0-9]+)[ \t]+UUXFER ver ",
        1,  2,  3,  0,  CASE_SENSITIVE,     NULL
    },
    {   NULL,
        0,  0,  0,  0,  IGN_CASE,   NULL
    }
};
*/

static  PART_RE Parts10[] =
{
    {   "^POST V.* ([^ \t]+).*\\(Part ([0-9]+)/([0-9]+)\\)",
        1,  2,  3,  0,  CASE_SENSITIVE,      NULL
    },
    {   NULL,
        0,  0,  0,  0,  IGN_CASE,   NULL
    }
};

static  IDENT   Hdr1[] =
{
    {   "^Subject:",                            Parts1,     NULL            },
    {   "^X-File-Name:",                        Parts3,     NULL            },
    {   "^X-Part:",                             Parts4,     NULL            },
    {   "^X-Part-Total:",                       Parts5,     NULL            },
    {   "^Uusplit-part:",                       Parts6,     NULL            },
    {   "^Uusplit-parts:",                      Parts7,     NULL            },
    {   NULL,                                   NULL,       NULL            }
};

static  IDENT   Body1[] =
{
    {   "^Subject:",                            Parts1,     NULL            },
    {   "^section [0-9]+ of uuencode [0-9]+\\.[0-9]+ of file [^ \t]+    by R.E.M.",
                                                Parts8,     NULL            },
    {   "^POST V",                              Parts10,    NULL            },
/*    {   "^[^ \t]+[ \t]+section[ \t]+[0-9]+/[0-9]+[ \t]+UUXFER ver ",
                                                Parts9,     NULL            },
*/
    {   NULL,                                   NULL,       NULL            }
};

/*=============================================================================
||  SEGMENT begin line regular expressions are defined below.
||
||  These can be set by command line switch.
=============================================================================*/

static  SEGMENT RnSegs[] =
{
    {   "^(Article[:]?|X-NEWS:) ",  Hdr1,   Body1,  NULL    },
    {   NULL,                       NULL,   NULL,   NULL    }
};

static  SEGMENT NnSegs[] =
{
    {   "^From[:]? ",               Hdr1,   Body1,  NULL    },
    {   NULL,                       NULL,   NULL,   NULL    }
};

static  SEGMENT EmailSegs[] =
{
    {   "^From ",                   Hdr1,   Body1,  NULL    },
    {   NULL,                       NULL,   NULL,   NULL    }
};

static  SEGMENT GroupsSegs[] =
{
    {   "^Newsgroups: ",            Hdr1,   Body1,  NULL    },
    {   NULL,                       NULL,   NULL,   NULL    }
};

static  SEGMENT         *Segments = RnSegs;
static  REG_EXP_NODE    *Begin = NULL;
static  REG_EXP_NODE    *End = NULL;
static  REG_EXP_NODE    *CrcRE = NULL;
static  char            *BeginStr = "^begin[ \t]+([0-7]+)[ \t]+([^ \t]+)";
static  char            *EndStr   = "^end[ \t]*$";
static  char            *CrcStr   = "^POST Crc: 0x([0-9a-fA-F]+)";

/*-----------------------------------------------------------------------------
| Routine   :   GetBinFlNm() --- Get the binary file name.
|
| Inputs    :   InFlPtr - Pointer to source file.
| Outputs   :   FlName  - Pointer to file name buffer.
-----------------------------------------------------------------------------*/

int     GetBinFlNm(FILE     *InFlPtr,
                   char     **RetStrs,
                   char     *FlName)
{
    auto        long        LnOfs;
    auto        int         RetVal;
    auto        char        *tp;
    auto        char        *sp;
    auto        int         OutLen;
    auto        char        Exten[5];
    auto        char        BeginName[FL_NM_SZ];

    /*  Externals used by this function.    */
    extern      BYTE        OutBfr[];
    extern      char        SegLine[];
    extern      char        InBfr[];
    extern      char        UULine[];
    extern      FILE        *ErrFile;
    extern      int         MsDosFileNms;

    /*  Extract the file name.    */
    for (tp = BeginName, sp = RetStrs[2];
         *sp && *sp != '\n' && *sp != ' ' && *sp != '\t';
        )
        *tp++ = *sp++;
    *tp = '\0';

    /*  Munge file name?    */
    RetVal = 0;
    if ( MsDosFileNms )
    {
        /*  Get the current file offset.    */
        LnOfs = ftell( InFlPtr );

        /*  Get next line and identify file type.   */
        *Exten = '\0';
        if (ReadLine(InFlPtr, InBfr, BFR_SIZE) == EOF)
        {
            fprintf(ErrFile,
                    "%s %d : Warning - Unexpected end of file in segment:\n",
                    __FILE__,
                    __LINE__);
            fprintf(ErrFile,
                    "\tSegment: '%s'\n",
                    SegLine);
            RetVal = 2;
        }
        else if (DecUULine(InBfr, &OutLen, OutBfr) == NOT_UU_LINE)
        {
            fprintf(ErrFile,
                    "%s %d : Warning - No UU line after begin.\n",
                    __FILE__,
                    __LINE__);
            fprintf(ErrFile,
                    "\tSegment: '%s'\n",
                    SegLine);
            RetVal = 2;
        }
        else
        {
            /*  Attempt to ID the file. */
            IdUUFile(OutBfr, OutLen, Exten);

            /*  Modify the file name to be MS-DOS compatible.   */
            ModifyFlNm(BeginName, Exten, FlName);
        }

        /*  Position file pointer to start of line. */
        if (fseek(InFlPtr, LnOfs, SEEK_SET) != 0)
        {
            fprintf(ErrFile,
                    "%s %d : Error - %s\n",
                    __FILE__,
                    __LINE__,
                    strerror( errno ));
            exit( 1 );
        }
    }
    else
        strcpy(FlName, BeginName);

#if defined(UNPOST_DEBUG)
printf("\tBinary File Name: '%s'\n", FlName);
#endif
    return( RetVal );
}

/*-----------------------------------------------------------------------------
| Routine   :   MatchEnd() --- Match a uuencode end line.
|
| Inputs    :   Line        - The line to attempt to match against.
-----------------------------------------------------------------------------*/

int     MatchEnd(char   *Line)
{
    auto    char    **RetStrs;

    /*  Attempt to match the line.  */
    return( ReMatch(Line, CASE_SENSITIVE, End, &RetStrs) );
}

/*-----------------------------------------------------------------------------
| Routine   :   MatchBegin() --- Match a uuencode begin line.
|
| Inputs    :   Line        - The line to attempt to match against.
| Outputs   :   RetStrs     - Returned sub-strings.
-----------------------------------------------------------------------------*/

int     MatchBegin(char     *Line,
                   char     ***RetStrs)
{
    /*  Attempt to match the line.  */
    return( ReMatch(Line, CASE_SENSITIVE, Begin, RetStrs) );
}

/*-----------------------------------------------------------------------------
| Routine   :   MatchSegment() --- Match a SEGMENT begin line.
|
| Inputs    :   Line    - The line to attempt to match against.
| Outputs   :   Hdr     - Pointer to header ID line RE's.
|               Body    - Pointer to body ID line RE's.
-----------------------------------------------------------------------------*/

int     MatchSegment(char       *Line,
                     IDENT      **Hdr,
                     IDENT      **Body)
{
    register    int     i;
    auto        char    **RetStrs;

    /*  Attempt to match the line.  */
    for (i = 0; Segments[i].ReExprStr; i++)
    {
        /*  Attempt to match one of the segment begin lines.    */
        if (ReMatch(Line,
                    CASE_SENSITIVE,
                    Segments[i].ReExpr,
                    &RetStrs) != 0)
        {
            *Hdr = Segments[i].Header;
            *Body = Segments[i].Body;
            return( 1 );
        }
    }

    /*  Return not matched. */
    Hdr = NULL;
    Body = NULL;
    return( 0 );
}

/*-----------------------------------------------------------------------------
| Routine   :   GetBinID() --- Get binary ID string.
|
| Inputs    :   SubStr  - Pointer to possible ID sub-string.
| Outputs   :   IDStr   - Pointer to ID string buffer.
-----------------------------------------------------------------------------*/

static
void    GetBinID(char   *SubStr,
                 char   *IDStr)
{
    auto        int     ExtSep;
    auto        char    *DestPtr;
    auto        char    *tp;

    extern      int     MsDosFileNms;

    /*  Filter string.  */
    FlNmFilter( SubStr );

    /*  Attempt to guess at a file name.    */
    for (tp = SubStr; *tp; )
    {
        /*  Skip white space.   */
        while (*tp == ' ' || *tp == '\t')
            tp++;

        /*  Get word. */
        for (DestPtr = IDStr, ExtSep = 0;
             *tp && *tp != ' ' && *tp != '\t';
             tp++)
        {
            /*  Check to see if this is and extension separator
            *   character, and if so, count how many.
            */
            if (*tp == EXT_SEP_CHAR)
                ExtSep++;

            /*  Copy character. */
            *DestPtr++ = *tp;
        }
        *DestPtr = '\0';

        /*  Does this look like a file name?    */
        if ( ExtSep )
        {
#if defined(UNPOST_DEBUG)
printf("\tBinary ID: '%s'\n", IDStr);
#endif
            return;
        }
    }

    /*  OK, we didn't find anything that looks like it could possibly
    *   be a file name, so munge all the pieces together.
    */
    for (tp = SubStr, DestPtr = IDStr; *tp; )
    {
        /*  Skip white space.   */
        if (*tp == ' ' || *tp == '\t')
            tp++;
        else
            *DestPtr++ = *tp++;
    }
    *DestPtr = '\0';

#if defined(UNPOST_DEBUG)
printf("\tBinary ID: '%s'\n", IDStr);
#endif
}

/*-----------------------------------------------------------------------------
| Routine   :   ParseIDLine() --- Extract the ID string, part number and
|               total number of parts from the ID line.
|
| Inputs    :   IDLine      - Pointer to ID line.
|               PartREs     - Array of part number parsing RE's.
| Outputs   :   Elements    - Aqquisition flags for the three items to
|                             parse out, ID string, Segment number and
|                             total number of segments.
|               SegInfo     - Pointer to segment information buffer.
-----------------------------------------------------------------------------*/

static
void    ParseIDLine(char        *IDLine,
                    PART_RE     *PartREs,
                    int         *Elements,
                    SEG_INFO    *SegInfo)
{
    register    int     i;
    auto        char    **RetStrs;
    auto        PART_RE *PartRec;
    auto        char    IDBfr[FL_NM_SZ];

    /*  Externals used in this function.    */
    extern      FILE    *ErrFile;

    /*  Seach for a matching part number parsing regular expression.    */
    for (PartRec = NULL, i = 0;
         PartREs[i].ReExpStr;
         i++)
    {
        /*  Does this RE match the ID line? */
        if (ReMatch(IDLine,
                    PartREs[i].Case,
                    PartREs[i].ReExpr,
                    &RetStrs) != 0)
        {
#if defined(UNPOST_DEBUG)
printf("\tExtract RE: /%s/\n", PartREs[i].ReExpStr);
#endif
            PartRec = PartREs + i;
            break;
        }
    }

    /*  If no match found, return.  */
    if (PartRec == NULL)
        return;

    /*  Get what elements we do not yet have.    */
    for (i = ID_STRING; i <= NO_SEGMENTS; i++)
    {
        /*  Get this element, if it is available.   */
        switch ( i )
        {
        case 0:     /*  Get ID string.  */
            /*  Check for no ID string.    */
            if (PartRec->IDStr == 0)
                break;

            /*  Extract ID string from sub string.  */
            GetBinID(RetStrs[ PartRec->IDStr ], IDBfr);

            /*  Check to see if there was an ID string or not.  */
            if (*IDBfr == '\0')
            {
                /*  Is there an alternate regular expression for
                *   extracting the binary ID string?
                */
                if (PartRec->AltIDStr > 0)
                {
                    /*  OK, try other side. */
                    GetBinID(RetStrs[ PartRec->AltIDStr ], IDBfr);
                    if (*IDBfr == '\0')
                        break;
                }
                else
                    break;
            }

            /*  Duplicate the ID string.    */
            if (SegInfo->IDString != NULL)
                free( SegInfo->IDString );
            SegInfo->IDString = StrDup( IDBfr );
            Elements[i] = 1;
            break;
        case 1:     /*  Get the segment number. */
            /*  Check for no segment number.    */
            if (PartRec->SegNo == 0)
                break;

            /*  Get the segment number.    */
            if ((SegInfo->SegNo = atoi( RetStrs[ PartRec->SegNo ] )) < 0)
                break;
            Elements[i] = 1;
            break;
        case 2:     /*  Get total number of segments.   */
            /*  Check for no total number of segments.  */
            if (PartRec->NoSegs == 0)
                break;

            /*  Get the total number of segments.  */
            if ((SegInfo->NoSegs = atoi( RetStrs[ PartRec->NoSegs ] )) <= 0)
                break;
            Elements[i] = 1;
            break;
        }
    }
}

/*-----------------------------------------------------------------------------
| Routine   :   IdSearch() --- Search for an ID line.
|
| Inputs    :   InFlPtr     - Input file pointer.
|               IdPtr       - Pointer to ID RE hierarchy for this SEGMENT.
| Outputs   :   Elements    - Check list for data elements.
|               IDLine      - Contains ID line.
|               UULnType    - Type of UU encoded line found.
|               RetStrs     - Returned sub strings from RE match.
|               SegInfo     - Pointer to segment information buffer.
|
| Returns   :   Returns one of:
-----------------------------------------------------------------------------*/

static
long    IdSearch(FILE       *InFlPtr,
                 IDENT      *IdPtr,
                 int        *Elements,
                 char       *IDLine,
                 CHK_UU_ENC *UULnType,
                 char       ***RetStrs,
                 SEG_INFO   *SegInfo)
{
    register    int         i;
    auto        long        LnOfs;
    auto        int         EncLen;
    auto        IDENT       *Hdr;
    auto        IDENT       *Body;
    extern      FILE        *ErrFile;

    /*  Search forwards through the file for the first ID line. */
    for ( ; ; )
    {
        /*  Get the current file offset.    */
        LnOfs = ftell( InFlPtr );

        /*  Get a line from the file.   */
        if (ReadLine(InFlPtr, IDLine, BFR_SIZE) == EOF)
        {
            LnOfs = PRS_NO_UU_LN;
            break;
        }

        /*  Is this a SEGMENT begin line?    */
        if ( MatchSegment(IDLine, &Hdr, &Body) )
        {
            /*  Position file pointer to start of line. */
            if (fseek(InFlPtr, LnOfs, SEEK_SET) != 0)
            {
                fprintf(ErrFile,
                        "%s %d : Error - %s\n",
                        __FILE__,
                        __LINE__,
                        strerror( errno ));
                exit( 1 );
            }

            /*  Return that no UU encoded line was found.   */
            LnOfs = PRS_NO_UU_LN;
            break;
        }

        /*  Is this a UUencoded line?   */
        *UULnType = ChkUULine(IDLine, RetStrs, &EncLen);
        if (*UULnType == IS_UU_LINE ||
            *UULnType == UU_BEGIN   ||
            *UULnType == UU_END)
        {
            /*  Did we miss getting a piece of data we would like to
            *   have?
            */
            if (Elements[SEGMENT_NO] == 0 && Elements[NO_SEGMENTS])
            {
                /*  Error message.  */
                fprintf(ErrFile,
                        "%s %d : Error - Got number of segments but not ",
                        __FILE__,
                        __LINE__);
                fprintf(ErrFile,
                        "segment number.\n");

                /*  Check for totally idiotic mess. */
                if (SegInfo->NoSegs == 1)
                {
                    /*  Attempt assumption. */
                    fprintf(ErrFile,
                            "\tNumber of Segments: %d\n",
                            SegInfo->NoSegs);
                    fprintf(ErrFile,
                            "\tAssuming Part 1 of 1\n");
                    SegInfo->SegNo = SegInfo->NoSegs = 1;
                }
                else
                    LnOfs = PRS_NO_SEG_NUM;
            }
            else if (Elements[SEGMENT_NO] && Elements[NO_SEGMENTS] == 0)
            {
                /*  Error message.  */
                fprintf(ErrFile,
                        "%s %d : Error - Got segment number but not number ",
                        __FILE__,
                        __LINE__);
                fprintf(ErrFile,
                        "of segments.\n");

                /*  Check segment number.   */
                if (SegInfo->SegNo == 1)
                {
                    /*  Attempt assumption. */
                    fprintf(ErrFile,
                            "Segment Number: %d\n\tAssuming Part 1 of 1\n",
                            SegInfo->SegNo);
                    SegInfo->SegNo = SegInfo->NoSegs = 1;
                }
                else
                    LnOfs = PRS_NO_NUM_SEGS;
            }
            else if (Elements[SEGMENT_NO] == 0 && Elements[NO_SEGMENTS] == 0)
                SegInfo->SegNo = SegInfo->NoSegs = 1;
            break;
        }

        /*  Is this an ID line? */
        for (i = 0; IdPtr[i].ReExprStr; i++)
        {
            /*  Does this line match?   */
            if (ReMatch(IDLine,
                        CASE_SENSITIVE,
                        IdPtr[i].ReExpr,
                        RetStrs) != 0)
            {
#if defined(UNPOST_DEBUG)
printf("\n\tID Line: '%s'\n", IDLine);
printf("\tBody RE: /%s/\n", IdPtr[i].ReExprStr);
#endif
                /*  Attempt to parse out one or more elements.  */
                ParseIDLine(IDLine, IdPtr[i].IdParts, Elements, SegInfo);
                break;
            }
        }

        /*  Is this a POST CRC line?    */
        if (ReMatch(IDLine,
                    IGN_CASE,
                    CrcRE,
                    RetStrs) != 0)
        {
            /*  Convert the string to an unsigned long. */
            SegInfo->PostCRC = (CRC_TYPE) HexToUL( (*RetStrs)[1] );
            SegInfo->ChkCRC = 1;
        }
    }

    /*  Return status.  */
    return( LnOfs );
}

/*-----------------------------------------------------------------------------
| Routine   :   Header() --- Search for legal header ID lines.
|
| Inputs    :   InFlPtr     - Input file pointer.
|               IdPtr       - Pointer to ID RE hierarchy for this SEGMENT.
| Outputs   :   Elements    - Checklist for needed pieces of information.
|               IDLine      - Contains ID line.
|               SegInfo     - Pointer to segment information buffer.
|
| Returns   :   Returns one of:
|                   PRS_NO_UU_LN    - For no uuencoded line found in segment.
|                   1L              - OK.
-----------------------------------------------------------------------------*/

static
long    Header(FILE         *InFlPtr,
               IDENT        *IdPtr,
               int          *Elements,
               char         *IDLine,
               SEG_INFO     *SegInfo)
{
    register    int         i;
    auto        char        **RetStrs;
    auto        char        *tp;
    extern      FILE        *ErrFile;

    /*  Search forwards through the file for the first ID line. */
    for ( ; ; )
    {
        /*  Is this an ID line? */
        for (i = 0; IdPtr[i].ReExprStr; i++)
        {
            /*  Does this line match?   */
            if (ReMatch(IDLine,
                        CASE_SENSITIVE,
                        IdPtr[i].ReExpr,
                        &RetStrs) != 0)
            {
#if defined(UNPOST_DEBUG)
printf("\n\tID Line: '%s'\n", IDLine);
printf("\tHeader RE: /%s/\n", IdPtr[i].ReExprStr);
#endif
                /*  Attempt to parse out one or more elements.  */
                ParseIDLine(IDLine, IdPtr[i].IdParts, Elements, SegInfo);
                break;
            }
        }

        /*  Get a line from the file.   */
        if (ReadLine(InFlPtr, IDLine, BFR_SIZE) == EOF)
            return( PRS_NO_UU_LN );

        /*  Check for a blank line, which is the header delimiter.  */
        for (tp = IDLine; *tp == ' ' || *tp == '\t'; tp++)
            ;
        if (*tp == '\0' || *tp == '\n')
            break;
    }

    /*  Return that no errors occured.  */
    return( 1L );
}

/*-----------------------------------------------------------------------------
| Routine   :   Parse() --- Parse out a SEGMENT and ID line.
|
| Inputs    :   InFlPtr     - Input file pointer.
| Outputs   :   SegLine     - Contains the segment line.
|               IDLine      - Contains ID line.
|               UULine      - Contains the first UU encoded line.
|               SegInfo     - Pointer to segment information buffer.
|
| Returns   :   Returns one of:
|                   PRS_NO_SEGMENT  - End of file found.
|                   PRS_NO_UU_LN    - No uuencoded line found in article.
|                   PRS_NO_ID_STR   - No ID string found at all.
|                   PRS_NO_BEGIN    - No uuencode begin line found in first
|                                     segment.
-----------------------------------------------------------------------------*/

long    Parse(FILE      *InFlPtr,
              char      *SegLine,
              char      *IDLine,
              SEG_INFO  *SegInfo)
{
    register    int         i;
    auto        long        LnOfs;
    auto        char        **RetStrs;
    auto        IDENT       *Hdr;
    auto        IDENT       *Body;
    auto        CHK_UU_ENC  UULnType;
    auto        int         Elements[NO_SEGMENTS + 1];
    auto        char        FlName[FL_NM_SZ];

    /*  Externals used by this function.    */
    extern      FILE        *ErrFile;
    extern      int         MsDosFileNms;

    /*  Initialize the elements array to show that we have none of the
    *   elements.
    */
    for (i = ID_STRING; i <= NO_SEGMENTS; i++)
        Elements[i] = 0;

    /*  Search forwards through the file for the first SEGMENT
    *   begin line.
    */
    for ( ; ; )
    {
        /*  Get the current file offset.    */
        LnOfs = ftell( InFlPtr );

        /*  Get a line from the file.   */
        if (ReadLine(InFlPtr, SegLine, BFR_SIZE) == EOF)
            return( PRS_NO_SEGMENT );

        /*  Is this a SEGMENT begin line?    */
        if ( MatchSegment(SegLine, &Hdr, &Body) )
        {
#if defined(UNPOST_DEBUG)
printf("Segment Begin: '%s'\n", SegLine);
#endif
            strcpy(IDLine, SegLine);
            break;
        }
    }

    /*  Initialize new segment. */
    SegInfo->SegOfs = LnOfs;

    /*  Process header block.   */
    LnOfs = Header(InFlPtr,
                   Hdr,
                   Elements,
                   IDLine,
                   SegInfo);
    if (LnOfs < 0L)
        return( LnOfs );

    /*  Process body to end of segment or first UU line.    */
    LnOfs = IdSearch(InFlPtr,
                     Body,
                     Elements,
                     IDLine,
                     &UULnType,
                     &RetStrs,
                     SegInfo);
    if (LnOfs < 0L)
        return( LnOfs );

    /*  Is this the begin line? */
    SegInfo->UUOfs = LnOfs;
    if (UULnType != UU_BEGIN)
    {
        /*  If this is segment number 1 and we did not find a begin line,
        *   that is BIG trouble, so report an error.
        */
        if (SegInfo->SegNo == 1)
        {
            fprintf(ErrFile,
                    "%s %d : Error - No begin line in first segment:\n",
                    __FILE__,
                    __LINE__);
            fprintf(ErrFile,
                    "\tSegment: '%s'\n",
                    SegLine);
            return( PRS_NO_BEGIN );
        }
        return( 0L );
    }

    /*  Get file name from begin line.  */
    GetBinFlNm(InFlPtr, RetStrs, FlName);
#if defined(UNPOST_DEBUG)
printf("\tFile Name: '%s'\n", FlName);
#endif

    /*  Return no errors occurred.  */
    if ( *FlName )
        SegInfo->FlName = StrDup( FlName );
    return( 0L );
}

/*-----------------------------------------------------------------------------
| Routine   :   FreeCfg() --- Free a configuration that was created by
|               reading a configuration file.
-----------------------------------------------------------------------------*/

static
void    FreeCfg(void)
{
    register    int         i;
    register    int         j;
    register    int         k;

    /*  If the default configuration is in place, do nothing, else
    *   free the old configuration.
    */
    if (Segments != NnSegs     && Segments != RnSegs &&
        Segments != GroupsSegs && Segments != EmailSegs)
    {
        /*  Free all ID prefix lists in SEGMENT list.   */
        for (i = 0; Segments[i].ReExprStr; i++)
        {
            /*  Free all ID part lists in ID prefix list.   */
            for (j = 0;
                 Segments[i].Header[j].ReExprStr;
                 j++)
            {
                /*  Free all part extraction RE's, etc. */
                for (k = 0;
                     Segments[i].Header[j].IdParts[k].ReExpStr;
                     k++)
                {
                     free( Segments[i].Header[j].IdParts[k].ReExpStr );
                     FreeReExpr( Segments[i].Header[j].IdParts[k].ReExpr );
                }

                /*  Free list memory and regular expression.    */
                free( Segments[i].Header[j].IdParts );
                free( Segments[i].Header[j].ReExprStr );
                (void) FreeReExpr( Segments[i].Header[j].ReExpr );
            }
            free( Segments[i].Header );

            /*  Free all ID part lists in ID prefix list.   */
            for (j = 0;
                 Segments[i].Body[j].ReExprStr;
                 j++)
            {
                /*  Free all part extraction RE's, etc. */
                for (k = 0;
                     Segments[i].Body[j].IdParts[k].ReExpStr;
                     k++)
                {
                     free( Segments[i].Body[j].IdParts[k].ReExpStr );
                     FreeReExpr( Segments[i].Body[j].IdParts[k].ReExpr );
                }

                /*  Free list memory and regular expression.    */
                free( Segments[i].Body[j].IdParts );
                free( Segments[i].Body[j].ReExprStr );
                (void) FreeReExpr( Segments[i].Body[j].ReExpr );
            }
            free( Segments[i].Body );

            /*  Free the regular expression graph for the segment.  */
            free( Segments[i].ReExprStr );
            (void) FreeReExpr( Segments[i].ReExpr );
        }

        /*  Free SEGMENT list.  */
        free( Segments );
    }
}

/*-----------------------------------------------------------------------------
| Routine   :   SetSegBegin() --- Set the segment begin line regular
|               expression.
|
| Inputs    :   SegType - Either 'e', 'g', 'n', 'm', 'r'.
-----------------------------------------------------------------------------*/

void    SetSegBegin(char    *SegType)
{
    register    int     i;

    /*  Free any previously allocated configurations (configurations
    *   read in from a config file only.
    */
    FreeCfg();

    /*  Determine which type, based on input string.    */
    switch ( tolower( *SegType ) )
    {
    case 'e':
        Segments = EmailSegs;
        break;
    case 'g':
        Segments = GroupsSegs;
        break;
    case 'n':
        Segments = NnSegs;
        break;
    case 'r':
    default:
        Segments = RnSegs;
        break;
    }

    /*  Compile SEGMENT begin RE's. */
    for (i = 0; Segments[i].ReExprStr; i++)
        if (Segments[i].ReExpr == NULL)
            Segments[i].ReExpr = ReCompile( Segments[i].ReExprStr );
}

/*-----------------------------------------------------------------------------
| Routine   :   CompCfg() --- Compile a configuration.
-----------------------------------------------------------------------------*/

static
void    CompCfg(void)
{
    register    int         i;
    register    int         j;
    register    int         k;
    auto        PART_RE     *PartPtr;
    auto        IDENT       *IdPtr;
    auto        SEGMENT     *SegPtr;

    /*  Compile the regular expressions.    */
    for (i = 0; Segments[i].ReExprStr; i++)
    {
        /*  Compile the unique SEGMENT line prefix. */
        SegPtr = Segments + i;
        if (SegPtr->ReExpr == NULL)
            SegPtr->ReExpr = ReCompile( SegPtr->ReExprStr );

        /*  Compile all Header ID line information.    */
        for (j = 0; SegPtr->Header[j].ReExprStr; j++)
        {
            /*  Compile the unique ID line prefix.  */
            IdPtr = SegPtr->Header + j;
            IdPtr->ReExpr = ReCompile( IdPtr->ReExprStr );

            /*  Compile the part number parsing RE's.   */
            for (k = 0; IdPtr->IdParts[k].ReExpStr; k++)
            {
                PartPtr = IdPtr->IdParts + k;
                PartPtr->ReExpr = ReCompile( PartPtr->ReExpStr );
            }
        }

        /*  Compile all Body ID line information.    */
        for (j = 0; SegPtr->Body[j].ReExprStr; j++)
        {
            /*  Compile the unique Body ID line prefix.  */
            IdPtr = SegPtr->Body + j;
            IdPtr->ReExpr = ReCompile( IdPtr->ReExprStr );

            /*  Compile the part number parsing RE's.   */
            for (k = 0; IdPtr->IdParts[k].ReExpStr; k++)
            {
                PartPtr = IdPtr->IdParts + k;
                PartPtr->ReExpr = ReCompile( PartPtr->ReExpStr );
            }
        }
    }
}

/*-----------------------------------------------------------------------------
| Routine   :   LoadCfg() --- Load a configuration file.
|
| Inputs    :   CfgFlNm - Configuration file name.
-----------------------------------------------------------------------------*/

void    LoadCfg(char   *CfgFlNm)
{
    extern      FILE        *ErrFile;

    /*  Check for reading a configuration file. */
    if (CfgFlNm == NULL || *CfgFlNm == '\0')
    {
        fprintf(ErrFile,
                "%s %d : Error - Missing configuraiton file name.\n",
                 __FILE__,
                __LINE__);
        return;
    }

    /*  Free any previously allocated configuration trees.  */
    FreeCfg();

    /*  Parse configuration file.   */
    Segments = ReadConfig( CfgFlNm );

    /*  Compile the configuration.  */
    CompCfg();
}

/*-----------------------------------------------------------------------------
| Routine   :   ParseInit() --- Compile regular expressions here that will
|               not change during run time, and compile the default
|               configuration.
-----------------------------------------------------------------------------*/

void    ParseInit(void)
{
    /*  Compile the RN default configuration.   */
    CompCfg();

    /*  Compile the UU encoding RE's.   */
    Begin = ReCompile( BeginStr );
    End = ReCompile( EndStr );
    CrcRE = ReCompile( CrcStr );
}


syntax highlighted by Code2HTML, v. 0.9.1