/* dbparser.c, -an RFFparser for db */

#include <exec/lists.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <proto/exec.h>
#include <proto/utility.h>
#include <proto/dos.h>
#include <clib/alib_protos.h>           /* NewList() */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>


#include "db.h"
#include "dbparser.h"
#include "dbGUI.h"
#include "dbRexx.h"     /* Only for the handling of the RXPORTNAME tag */

#define BUFSIZE 65536

#define Rewind(fp) Seek(fp, 0, OFFSET_BEGINNING)

char *RFFTagNames[] = {
        "dummy",
        "@RFF",
        "TYPE",
        "FLEN",
        "LNAM",
        "NAME",
        "SIZE",
        "OFFS",
        "NEXT",
        "TABSIZE",
        "RXFILE",
        "RXSTRING",
        "AUTORXFILE",
        "AUTORXSTRING",
        "NEWRECORDRXFILE",
        "NEWRECORDRXSTRING",
        "FTYP",
        "CENT",
        "SFMT",
        "RXPORTNAME",
        "PLACE",
        "ROWS",
        "XPOS",
        "YPOS",
        NULL
};

BPTR InFile;

void DeleteTag(struct RFFTag *ot)
{
        if (ot) {
                if (ot->Name) FreeMem(ot->Name,strlen(ot->Name)+1);
                if (ot->Data) FreeMem(ot->Data,strlen(ot->Data)+1);
                FreeMem(ot,sizeof(struct RFFTag));
        }
}

void DeleteRFFLine(struct RFFLine *ol)
{
        if (ol) {
                if (ol->Line) FreeMem(ol->Line,strlen(ol->Line)+1);
                FreeMem(ol,sizeof(struct RFFLine));
        }
}

struct RFFLine *NewRFFLine(const char *line)
{
        /* holds unknown RFF lines */
        struct RFFLine *nl;

        if (!(nl = AllocMem(sizeof(struct RFFLine),0))) return NULL;
        if (!(nl->Line = AllocMem(strlen(line)+1,0))) {
                DeleteRFFLine(nl);
                return NULL;
        }
        nl->mln.mln_Pred = NULL;
        nl->mln.mln_Succ = NULL;
        strcpy(nl->Line,line);

        return nl;
}

struct RFFTag *NewTag(const char *name, int nlen, const char *data, int dlen)
{
        struct RFFTag *nt;
        int i;

        if (!(nt = AllocMem(sizeof(struct RFFTag),MEMF_CLEAR))) return NULL;
        if (!(nt->Name = AllocMem(nlen+1,0))) {
                DeleteTag(nt);
                return NULL;
        }
        if (!(nt->Data = AllocMem(dlen+1,0))) {
                DeleteTag(nt);
                return NULL;
        }

        stccpy(nt->Name,name,nlen+1);
        stccpy(nt->Data,data,dlen+1);
        nt->ID = UNKNOWN;

        for(i=1; RFFTagNames[i]; i++)
                if (!Stricmp((STRPTR)nt->Name,(STRPTR)RFFTagNames[i])) {
                        nt->ID = i;
                        break;
                }
        return nt;
}


struct FldInfo *NewFldInfo(void)
{
        /* Allocates and initializes a FldInfo struct with default values */
        struct FldInfo *f;

        if (!(f = AllocMem(sizeof(struct FldInfo),0))) return NULL;
        NewList((struct List *)&f->FldTags);
        f->Next = NULL;
        f->Len = DEFMAXFLDLEN;
        /* f->Type = something */
        return f;
}

/* // Nice, but later on maybe. See DeletePro()
void DeleteRxInfo(struct RxInfo *ri)
{
        struct RFFTag *ot;
        while (ot = (struct RFFTag *)RemHead((struct List *)&ri->RxTags))
                DeleteTag(ot);
        FreeMem(ri, sizeof(struct RxInfo));
}
*/

struct RxInfo *NewRxInfo(void)
{
        /* Allocates and initializes an RxInfo struct with default values */
        struct RxInfo *ri;

        if (ri = AllocMem(sizeof(struct RxInfo),0)) {
                NewList((struct List *)&ri->RxTags);
                ri->Next = NULL;
        }
        return ri;
}

void DeleteVisFldInfo(struct VisFldInfo *vf)
{
        struct RFFTag *ot;
        if (!vf) return;
        if (vf->CEnt) FreeMem(vf->CEnt, (vf->NEnt+1)*sizeof(char *));

        while (ot = (struct RFFTag *)RemHead((struct List *)&vf->VisTags))
                DeleteTag(ot);
        FreeMem(vf, sizeof(struct VisFldInfo));
}

struct VisFldInfo *NewVisFldInfo(void)
{
        /* Allocates and initializes a VisFldInfo struct with default values */
        struct VisFldInfo *vf;
        struct IntuiText label = { 1,0,JAM1,0,0,NULL,NULL,NULL };
        
        if (!(vf = AllocMem(sizeof(struct VisFldInfo),0))) return NULL;
        
        NewList((struct List *)&vf->VisTags);
        vf->Next = NULL;
        vf->Gadget = NULL;
        vf->Name[0] = '\0';
        vf->Offset = 0;
        vf->VisLen = 25;
        vf->VisSep = '\n';
        vf->FTyp = _STRING;
        vf->Code = 0;
        vf->CEnt = NULL;
        vf->NEnt = 0;
        
        vf->Label = label;
        vf->Label.IText = vf->Name;

        return vf;
}

/* Support functions for RFFOut */
struct RFFTag *FirstTag(struct MinList *list)
{
        struct RFFTag *tag = (struct RFFTag *)list->mlh_Head;
        if (!tag->mln.mln_Succ) return NULL;    /* The MinList node */
        return tag;
}

struct RFFTag *NextTag(struct RFFTag *tag)
{
        tag = (struct RFFTag *)tag->mln.mln_Succ;
        if (!tag->mln.mln_Succ) return NULL;    /* The MinList node */
        return tag;
}

struct RFFTag *NextSameTag(struct RFFTag *tag)
{
        short int oldID = tag->ID;

        for (;;) {
                tag = (struct RFFTag *)tag->mln.mln_Succ;
                if (!tag->mln.mln_Succ) return NULL;    /* The MinList node */
                if (tag->ID == oldID) return tag;
        }
}

struct VisFldInfo *CopyVisFldInfo(struct VisFldInfo *vf)
{
        struct RFFTag *tag, *nt;
        struct VisFldInfo *newvf;
        int i;

        if (!(newvf = NewVisFldInfo())) return NULL;

        *newvf = *vf;   /* Shallow copy. We have to do more than that */
        newvf->Label.IText = newvf->Name;

        NewList((struct List *)&newvf->VisTags);
        newvf->CEnt = NULL;

        for (tag = FirstTag(&vf->VisTags); tag; tag = NextTag(tag)) {
                if (!(nt = NewTag(tag->Name, strlen(tag->Name), tag->Data, strlen(tag->Data)))) {
                        DeleteVisFldInfo(newvf);
                        return NULL;
                }
                else AddTail((struct List *)&newvf->VisTags, (struct Node *)nt);
        }

        if (tag = SearchTag(CurrentPro, newvf, NULL, CENT)) {
                if (!(newvf->CEnt =
                 AllocMem((vf->NEnt+1)*sizeof(char *), 0)))
                        {
                                DeleteVisFldInfo(newvf);
                                return NULL;
                        }
                for (i=0; tag ;i++, tag = NextSameTag(tag))
                        newvf->CEnt[i] = tag->Data;
                newvf->CEnt[i] = NULL;  /* Nullterm cycleentrylist */
        }
        return newvf;
}


struct RFFTag *CreateAndAddTag(struct MinList *list, short int id, char *newdata)
{
        struct RFFTag *tag;

        if (!(tag = NewTag(RFFTagNames[id], strlen(RFFTagNames[id]),
                                                         newdata, strlen(newdata)))) return NULL;
        AddTail((struct List *)list, (struct Node *)tag);
        return tag;
}


struct RFFTag *UpdateTag(struct MinList *list, short int id, char *newdata)
{
        /* Updates tag with ID=id in RFFTaglist list. Creates tag if missing */
        /* Returns updated tag or NULL if memory error */

        struct RFFTag *tag;

        if (!(tag = FindTag(list, id))) {                        /* Create new tag */
                if (!(tag = NewTag(RFFTagNames[id], strlen(RFFTagNames[id]),
                                                                 newdata, strlen(newdata)))) return NULL;
                AddTail((struct List *)list, (struct Node *)tag);
        }
        else {  /* old tag found */
                int dlen = strlen(newdata);
                char *to;
                if (!(to = AllocMem(dlen+1,0))) return NULL;
                if (tag->Data) FreeMem(tag->Data,strlen(tag->Data)+1);
                tag->Data = to;
                stccpy(tag->Data,newdata,dlen+1);
        }
        return tag;
}

void WriteVisTags(struct VisFldInfo *vf)
{
        /* Copy information from vf to vf's tags */
        /* i.e. write vf's Name, Offset, VisLen & VisSep to the name, offs, size & next tags */

        char buf[10];   /* Well enough */

        UpdateTag(&vf->VisTags, NAME, vf->Name);
        sprintf(buf, "%d", vf->Offset);
        UpdateTag(&vf->VisTags, OFFS, buf);
        sprintf(buf, "%d", vf->VisLen);
        UpdateTag(&vf->VisTags, SIZE, buf);

        switch (vf->VisSep) {
                case ' ':
                        UpdateTag(&vf->VisTags, NEXT, STR_SPACE); break;
                case '\t':
                        UpdateTag(&vf->VisTags, NEXT, STR_TAB); break;
                case '\f':
                        UpdateTag(&vf->VisTags, NEXT, STR_PARA); break;
                case '\n':
                        UpdateTag(&vf->VisTags, NEXT, "nl"); break;
        }
}


int ReadVisTags(struct Pro *Pr, struct VisFldInfo *vf)
{
        /* Parse vf's tags and fill vf. If information is incorrect, return RFF_ERR */
        /* Information is incorrect when there is no corresponding field to an offset, */
        /* or when there is a cycle gadget with no cycle entries. */

        struct RFFTag *nt;
        struct FldInfo *f;
        int i;

        if (nt = FindTag(&vf->VisTags, OFFS))
                vf->Offset = atoi(nt->Data);
                
        if (nt = SearchTag(Pr, vf, NULL, SIZE)) vf->VisLen = atoi(nt->Data);
        if (nt = SearchTag(Pr, vf, NULL, NEXT)) {
                if (!Stricmp(nt->Data,"space")) vf->VisSep = ' ';
                else if (!Stricmp(nt->Data,"tab")) vf->VisSep = '\t';
                else if (!Stricmp(nt->Data,"para")) vf->VisSep = '\f';
        }

        /* Delete old cycle array if there was one */
        if (vf->CEnt) {
                FreeMem(vf->CEnt, (vf->NEnt+1)*sizeof(char *));
                vf->CEnt = NULL;
                vf->NEnt = 0;
        }

        if (nt = SearchTag(Pr, vf, NULL, FTYP)) {       /* Build new cycle array */
                if (!Stricmp(nt->Data,"cycle")) {
                        if (nt = SearchTag(Pr, vf, NULL, CENT)) {
                                for (; nt; ++vf->NEnt, nt = NextSameTag(nt));
                                /* Now vf->NEnt holds the number of cycleentries */
                                if (nt = SearchTag(Pr, vf, NULL, CENT)) {
                                        if (!(vf->CEnt =
                                         AllocMem((vf->NEnt+1)*sizeof(char *), 0)))
                                                return MEM_ERR;
                                        for (i=0; nt ;i++, nt = NextSameTag(nt))
                                                vf->CEnt[i] = nt->Data;
                                        vf->CEnt[i] = NULL;     /* Nullterm cycleentrylist */
                                }
                        }
                        else return RFF_ERR;    /* There must be atleast one choice */
                }
        }
        f = GetFldInfo(Pr, vf->Offset);
        if (nt = FindTag(&vf->VisTags, NAME))
                stccpy(vf->Name, nt->Data, FIELDNAMELENGTH);
        else if (f) strcpy(vf->Name,f->Name);

        if (!f) return RFF_ERR; /* Finally check that there is a corresponding FldInfo */
        return 0;
}

/* End of support functions */


struct RFFTag *FindTag(struct MinList *list, short int id)
{
        struct RFFTag *tag;
        for (tag = (struct RFFTag *)list->mlh_Head; tag->mln.mln_Succ;
         tag = (struct RFFTag *)tag->mln.mln_Succ) if (id == tag->ID) return tag;
        return NULL;
}

static struct RFFTag *FindTags(struct MinList *list, short tag1ID, short tag2ID)
{
        struct RFFTag *tag;
        if (!(tag = FindTag(list, tag1ID)))
                if (tag2ID) tag = FindTag(list, tag2ID);
        return tag;
}

struct RFFTag *SearchTags(struct Pro *Pr, struct VisFldInfo *vf, Where *where, short tag1ID, short tag2ID)
{
        struct RFFTag *tag;
        Where dummydest;
        if (!where) where = &dummydest;

        /* Find the current VisualField */
        if (!vf && !(vf = GetVisFldInfo(Pr->CurrentLayout, LastGad))) return NULL;

        if (tag = FindTags(&vf->VisTags, tag1ID, tag2ID)) *where = VIEW_LOCAL;
        else if (tag = FindTags(&Pr->CurrentLayout->LayTags, tag1ID, tag2ID)) *where = VIEW_GLOBAL;
        else if (tag = FindTags(&GetFldInfo(Pr, vf->Offset)->FldTags, tag1ID, tag2ID)) *where = PRO_LOCAL;
        else if (tag = FindTags(&CurrentPro->ProTags, tag1ID, tag2ID)) *where = PRO_GLOBAL;

        return tag;
}

struct RFFTag *SearchTag(struct Pro *Pr, struct VisFldInfo *vf, Where *where, short tagID)
{
        struct RFFTag *tag;
        Where dummydest;
        if (!where) where = &dummydest;

        /* Find the current VisualField */
        if (!vf && !(vf = GetVisFldInfo(Pr->CurrentLayout, LastGad))) return NULL;

        if (tag = FindTag(&vf->VisTags, tagID)) *where = VIEW_LOCAL;
        else if (tag = FindTag(&Pr->CurrentLayout->LayTags, tagID)) *where = VIEW_GLOBAL;
        else if (tag = FindTag(&GetFldInfo(Pr, vf->Offset)->FldTags, tagID)) *where = PRO_LOCAL;
        else if (tag = FindTag(&CurrentPro->ProTags, tagID)) *where = PRO_GLOBAL;

        return tag;
}


struct FldInfo *GetFldInfo(struct Pro *Pr, short Offset)
{
        /* Returns pointer to a FldInfo struct, returns NULL if not found */

        struct FldInfo *f, *old=NULL;
        int i;
        if (!Pr) return NULL; /* Safety, Should never happen */
        for (f = Pr->FirstFldInfo, i=0; i<=Offset; f = f->Next, i++) {
                if (!f) return NULL;
                else old = f;
        }
        return old;
}

struct VisFldInfo *GetVisFldInfo(struct Layout *Lay, struct Gadget *g)
{
        struct VisFldInfo *vf = Lay->FirstVisFldInfo;
        for (;vf ;vf = vf->Next) if (vf->Gadget == g) break;
        return vf;
}

__inline char *SkipSpc(const char *s)
{
        /* Returns a pointer to the first non-space character or a pointer to NULL */
        /* A comma is also considered a space */
        /* if no non-space characters are found */
        
        while (*s && (*s == ' ' || *s == ',')) s++;
        return s;
}


struct RFFTag *GetTag(const char **cursor)
{
        /* Takes a comma and tabseparated RFFLine and returns an RFFTag */
        /* Starts searching from cursor which is also updated after each call */
        /* Will return NULL when hitting a \t or \0 character */

        struct RFFTag *nt = NULL;
        const char *brkchar, *nameptr, *dataptr = NULL;
        int namelen, datalen;

        *cursor = SkipSpc(*cursor);

        if (**cursor == '\t') { 
                (*cursor)++;
                return NULL;
        }
        
        /* Name part*/
        if (brkchar = strpbrk(*cursor,"= ")) {
                namelen = brkchar - *cursor;
                nameptr = *cursor;
                *cursor = brkchar+1;

                /* Data part */
                if (**cursor == '\"') { /* "Quotes" */
                        if (brkchar = strpbrk(++(*cursor),"\"")) {      /* The other " */
                                datalen = brkchar - *cursor;
                                dataptr = *cursor;
                                *cursor = brkchar+1;
                        }
                }
                else {  /* No quotes */
                        if (brkchar = strpbrk(*cursor,", \t\n")) datalen = brkchar - *cursor;
                        else datalen = strlen(*cursor);
                        dataptr = *cursor;
                        *cursor = brkchar;
                }
                if (dataptr) nt = NewTag(nameptr,namelen,dataptr,datalen);
        }
        return nt;
}


/*
BOOL PutTag(struct RFFTag *tag, BPTR fp)
{
        if (FPuts(fp, tag->Name) == EOF) return FALSE;
        if (FPutC(fp, '=') == EOF) return FALSE;
        if (tag->Data && strlen(tag->Data)) {
                if (FPuts(fp, tag->Data) == EOF) return FALSE;
        }
        else if (FPuts(fp, "\"\"") == EOF) return FALSE;

        return TRUE;
}
*/

BOOL PutTag(struct RFFTag *tag, BPTR fp)
{
        BOOL quotes = FALSE;
        if (FPuts(fp, tag->Name) == EOF) return FALSE;
        if (FPutC(fp, '=') == EOF) return FALSE;
        if (tag->Data && strlen(tag->Data)) {
                if (strpbrk(tag->Data," ,\t")) {
                        quotes = TRUE;
                        if (FPutC(fp, '"') == EOF) return FALSE;
                }
                if (FPuts(fp, tag->Data) == EOF) return FALSE;
                if (quotes) if (FPutC(fp, '"') == EOF) return FALSE;
        }
        else if (FPuts(fp, "\"\"") == EOF) return FALSE;

        return TRUE;
}


BOOL PutTags(struct RFFTag *tag, BPTR fp)
{
        if (!PutTag(tag,fp)) return FALSE;
        while (tag = NextTag(tag)) {
                if (FPutC(fp, ',') == EOF) return FALSE;
                if (!PutTag(tag,fp)) return FALSE;
        }
        return TRUE;
}

BOOL RFFOut(struct Pro *Pr, BPTR fp)
{
        /* Writes all RFFtags to disk */

        struct Layout *lay;
        struct FldInfo *f;
        struct RxInfo *ri;
        struct VisFldInfo *vf;
        struct RFFLine *rl;
        struct RFFTag *tag;

        /* Save schema:
                ProTags, all FldTags, \n 
                RxTags, \n
                for all layouts: LayTags, all VisTags \n
                (UnknownLines, \n)
        */

ProTags:
        if (!(tag = FirstTag(&Pr->ProTags))) goto RxTags;
        if (!PutTags(tag,fp)) return FALSE;

FldTags:
        for (f = Pr->FirstFldInfo; f; f = f->Next) {
                if (FPutC(fp, '\t') == EOF) return FALSE;
                if (!(tag = FirstTag(&f->FldTags))) continue;
                if (!PutTags(tag,fp)) return FALSE;
        }
        if (FPutC(fp, '\n') == EOF) return FALSE;

RxTags:
        for (ri = Pr->FirstRxInfo; ri; ri = ri->Next) {
                if (ri != Pr->FirstRxInfo && FPutC(fp, '\t') == EOF) return FALSE;
                if (!(tag = FirstTag(&ri->RxTags))) continue;
                if (!PutTags(tag,fp)) return FALSE;
        }
        if (Pr->FirstRxInfo && FPutC(fp, '\n') == EOF) return FALSE;

Layouts:
        for (lay = Pr->FirstLayout; lay; lay = lay->NextLayout) {
Laytags:
                if (!(tag = FirstTag(&lay->LayTags))) continue;
                if (!PutTags(tag,fp)) return FALSE;
VisTags:
                for (vf = lay->FirstVisFldInfo; vf; vf = vf->Next) {
                        if (FPutC(fp, '\t') == EOF) return FALSE;
                        if (!(tag = FirstTag(&vf->VisTags))) continue;
                        if (!PutTags(tag,fp)) return FALSE;
                }
                if (FPutC(fp, '\n') == EOF) return FALSE;
        }

UnknownLines:
        for (rl = (struct RFFLine *)Pr->UnknownLines.mlh_Head; rl->mln.mln_Succ;
         rl = (struct RFFLine *)rl->mln.mln_Succ) {
                if (FPuts(fp, rl->Line) == EOF) return FALSE;
         }

        return TRUE;
}


BOOL DoASCIIParsing(struct Pro *Pr)
{
        /* Build default VisFldInfos if file lacks recognizable RFF lines */

        struct VisFldInfo *vf, *oldv = NULL;
        struct FldInfo *f;
        short off;

        if (!NewLayout(Pr)) return FALSE;

        /* Get the layoutname from the project name */
        stccpy(Pr->CurrentLayout->Name, Pr->Name, LAYOUTNAMELENGTH);

        /* Add an @rff=1.1 and a type=form tag */
        CreateAndAddTag(&Pr->CurrentLayout->LayTags, RFF, "1.1");
        CreateAndAddTag(&Pr->CurrentLayout->LayTags, TYPE, "form");
        
        for (f = Pr->FirstFldInfo, off = 0; f; f = f->Next, off++) {
                if (!(vf = NewVisFldInfo())) return FALSE;
                strcpy(vf->Name,f->Name);                       /* Same name as the internal names */
                vf->Offset = off;
                WriteVisTags(vf);
        
                /* Finally fix the links */
                if (!oldv) {
                        oldv = vf;
                        Pr->CurrentLayout->FirstVisFldInfo = vf;
                }
                else {  /* Fix backward links */
                        oldv->Next = vf;
                        oldv = vf;
                }
        }
        return TRUE;
}

int DoParsing(BPTR fp, struct Pro *Pr, char *buffer)
{
        /* Do the actual parsing */
        /* Return number of RFF lines found (both known and unknown */
        /* Negative numbers indicate errors */

        char *cursor;
        int n,len;
        struct VisFldInfo *vf, *oldv;
        struct FldInfo *f, *old = NULL;
        struct RFFTag *nt;
        struct RFFLine *rl;
        short defoffset;
        char *brkchar;
        int ret;
        
        /* Read first line and build the FldInfo list */
        if (!FGets(fp, buffer,BUFSIZE)) return FILE_ERR;
        cursor = buffer;
        while (brkchar = strpbrk(cursor,"\t\n")) {
                len = brkchar - cursor;
                if (len >= FIELDNAMELENGTH) return FILE_ERR;
                if (!(f = NewFldInfo())) return MEM_ERR;
                stccpy(f->Name, cursor,len+1);

                if (!old) {
                        old = f;
                        Pr->FirstFldInfo = f;
                }
                else {  /* Fix previous links */
                        old->Next = f;
                        old = f;
                }
                cursor += len+1;
        }
        
        /* Parse the RFF lines */

        for (n=0;;++n) {
                if (!FGets(fp, buffer,BUFSIZE)) return n;
                cursor=buffer;

                /* In RFF1.x The first two tags has to be @RFF and TYPE */
                if (!(nt = GetTag(&cursor))) return n;  /* Not an RFF line */
                if (nt->ID != RFF) {                                                    /* Not an RFF line */
                        DeleteTag(nt);
                        return n;
                }
                if (Strnicmp(nt->Data,"1.",2)) {                                                /* Unknown RFF line */
                        DeleteTag(nt);
                        if (rl = NewRFFLine(buffer))
                                AddTail((struct List *)&Pr->UnknownLines, (struct Node *)rl);
                        continue;
                }
                /* It's at least an RFF 1.1 line... */
                AddTail((struct List *)&Pr->ProTags, (struct Node *)nt);

                if (!(nt = GetTag(&cursor))) return n;                  /* no more tags?? */
                if (nt->ID != TYPE) {                                                           /* Unknown RFF line */
                        DeleteTag(nt);
                        DeleteTag((struct RFFTag *)RemTail((struct List *)&Pr->ProTags));
                        if (rl = NewRFFLine(buffer))
                         AddTail((struct List *)&Pr->UnknownLines, (struct Node *)rl);
                        continue;
                }
                
                /* Handle the different types */

                if (!Stricmp(nt->Data,"internal")) {
                        AddTail((struct List *)&Pr->ProTags, (struct Node *)nt);
                
                        /* Add rest of tags till we hit the first \t char */
                        while (nt = GetTag(&cursor))
                                AddTail((struct List *)&Pr->ProTags, (struct Node *)nt);

                        /* Work thru all tags adding information to the FldInfo structs */
                        for (f = Pr->FirstFldInfo; f; f = f->Next) {
                                while (nt = GetTag(&cursor)) {
                                        AddTail((struct List *)&f->FldTags, (struct Node *)nt);
                                }
                                if (nt = FindTag(&f->FldTags, FLEN)) f->Len = atoi(nt->Data);
                        }

                        /* Change the portname of this project if the RXPORTNAME tag is found */
                        if (nt = FindTag(&Pr->ProTags, RXPORTNAME)) {
                                if (MyRexxPort) CloseRexxPort(MyRexxPort);
                                if ((MyRexxPort = SetupRexxPort(nt->Data)) == NULL) return MEM_ERR;
                        }
                }

                else if (!Stricmp(nt->Data,"form")) {
                        if (!NewLayout(Pr)) {
                                DeleteTag(nt);
                                return MEM_ERR;
                        }
                        AddHead((struct List *)&Pr->CurrentLayout->LayTags, RemTail((struct List *)&Pr->ProTags));
                        AddTail((struct List *)&Pr->CurrentLayout->LayTags, (struct Node *)nt);

                        /* Add rest of tags till we hit the first \t char */
                        while (nt = GetTag(&cursor))
                                AddTail((struct List *)&Pr->CurrentLayout->LayTags, (struct Node *)nt);

                        /* Get the layoutname from the taglist */
                        if (nt = FindTag(&Pr->CurrentLayout->LayTags, LNAM))
                                stccpy(Pr->CurrentLayout->Name, nt->Data, LAYOUTNAMELENGTH);
                        
                        /* Read window position tags */
                        {
                                struct RFFTag *xpos, *ypos;
                                if (xpos = FindTag(&Pr->CurrentLayout->LayTags, XPOS))
                                        Pr->CurrentLayout->XPos = atoi(xpos->Data);
                                if (ypos = FindTag(&Pr->CurrentLayout->LayTags, YPOS))
                                        Pr->CurrentLayout->YPos = atoi(ypos->Data);
                        }
                        /* Work thru all tags building VisFldInfo structs along the way */
                        for (oldv=0, defoffset=0;;defoffset++) {
                                if (!(nt = GetTag(&cursor))) break;
                                if (!(vf = NewVisFldInfo())) {
                                        DeleteTag(nt);
                                        return MEM_ERR;
                                }
                                /* Fix the links now so we don't loose it if some error occurs */
                                if (!oldv) {
                                        oldv = vf;
                                        Pr->CurrentLayout->FirstVisFldInfo = vf;
                                }
                                else {  /* Fix backward links */
                                        oldv->Next = vf;
                                        oldv = vf;
                                }
                                
                                AddTail((struct List *)&vf->VisTags, (struct Node *)nt);
                                while (nt = GetTag(&cursor))
                                        AddTail((struct List *)&vf->VisTags, (struct Node *)nt);

                                /* Add values from the RFF tags */
                                vf->Offset = defoffset;
                                if ((ret = ReadVisTags(Pr, vf)) < 0) return ret;
                                defoffset = vf->Offset;
                        }
                }
                else if (!Stricmp(nt->Data,"rxmenu")) {
                        struct RxInfo *ri;
                        if (!(Pr->FirstRxInfo = NewRxInfo())) return MEM_ERR;
                        AddHead((struct List *)&Pr->FirstRxInfo->RxTags, RemTail((struct List *)&Pr->ProTags));
                        AddTail((struct List *)&Pr->FirstRxInfo->RxTags, (struct Node *)nt);
                        /* Add rest of tags till we hit the first \t char */
                        while (nt = GetTag(&cursor))
                                AddTail((struct List *)&Pr->FirstRxInfo->RxTags, (struct Node *)nt);

                        /* Work thru all tags building RxInfo structs along the way */
                        ri = Pr->FirstRxInfo;
                        while ((nt = GetTag(&cursor)) && (ri = ri->Next = NewRxInfo())) {
                                AddTail((struct List *)&ri->RxTags, (struct Node *)nt);
                                while (nt = GetTag(&cursor))
                                        AddTail((struct List *)&ri->RxTags, (struct Node *)nt);
                        }
                        if (nt) {
                                DeleteTag(nt);
                                return MEM_ERR;
                        }
                }
                else {                                                          /* Other unknown RFF types */
                        if (rl = NewRFFLine(buffer))
                                AddTail((struct List *)&Pr->UnknownLines, (struct Node *)rl);
                        DeleteTag((struct RFFTag *)RemTail((struct List *)&Pr->ProTags));
                        DeleteTag(nt);
                        continue;
                }
        }
}


BOOL IsBinary(BPTR fp)
{
        /* If the file contains at least one \0 character it's considered binary */
        int d;
        Rewind(fp);                                     // The last FGets() might have read past the file
        while ((d = FGetC(fp)) != EOF)
                if (!d) return TRUE;
        return FALSE;
}


int RFFParse(struct Pro *Pr, BPTR fp)
{
        /* Checks if fp is a valid RFF file. If so. The RFF tags will be parsed */
        /* and inserted into the project. */
        /* When finished, the next character read will be the first non-RFF line */

        char *buffer;
        int n;  /* Retval from DoParsing(). Number of RFF lines in file */

        /* Allocate enough space for one line */
        if (!(buffer = AllocMem(BUFSIZE,0))) return MEM_ERR;

        n = DoParsing(fp, Pr, buffer);
        Pr->CurrentLayout = Pr->FirstLayout;    

        if (n > 0) {    /* Some RFF lines exist */
                Rewind(fp);
                while(1 + n--) FGets(fp, buffer, BUFSIZE);      /* first line and RFF lines */
        }
        else if (n == 0) {
                if (IsBinary(fp)) {
                        FreeMem(buffer,BUFSIZE);
                        return FILE_ERR;
                }
                else {                                                                                          /* An ASCII file */
                        Rewind(fp);
                        FGets(fp, buffer,BUFSIZE);                                      /* Skip 1st line */
                }
        }
        else {
                FreeMem(buffer,BUFSIZE);                                                /* Some error */
                return n;
        }

        FreeMem(buffer,BUFSIZE);
        if (!Pr->FirstLayout)           /* There has to be at least one layout */
                if (!DoASCIIParsing(Pr)) return MEM_ERR; /* Build default VisFldInfos */

        return 0;
}
