/******************************************************************************
*
* Projet    : DilibDam
* Module    : DamRecord
* Fichier   : DamRecord.c
* Auteur    : Ducloy
*
****************************************************************************
*
* Copyright (c) 1994 CNRS/CRIN & INRIA Lorraine
* 
****************************************************************************
*				     DamRecord.h 
*
*     DamRecord : Structure intermédiaire pour l'input de données SGML
*
****************************************************************************
*  Notes de programmation les positions sont repérées par offset
*  (la localisation du Buffer n'est pas stabilisée avant la fin
*  de la lecture d'un record)
****************************************************************************/
#include "DamRecord.h"
#include "RegExp.h"
#include "Except.h"

static RegExp *RE_doctype;
static RegExp *RE_SgmlId;
static int    firstCall=1;
Buffer *internalBuffer;

void DamRecordInitModule()
{
  firstCall=0;
  RE_doctype=RegExpCreate("[ ]*[Dd][Oo][Cc][Tt][Yy][Pp][Ee]");
  RE_SgmlId=RegExpCreate("^[^ =/><\t][^ =/><\t]*");
  internalBuffer=BufferCreate(200,200);
}

DamRecordField *DamRecordField_Create()
{
  DamRecordField *field;
  if (firstCall)DamRecordInitModule();
  field = (DamRecordField *)malloc (sizeof(DamRecordField));
  field->next=NULL;
  field->type='u';
  field->beginField=NULL;
  return field;
}

DamRecordField *DamRecordField_Get(rec)
     DamRecord *rec;
{
  DamRecordField *field;
  if (rec->firstFreeList)
    { 
      field=rec->firstFreeList;
      rec->firstFreeList=field->next;
      field->next=NULL;
    }
  else
    {
      field=DamRecordField_Create();
    }
  if (rec->first)
    {
      rec->last->next=field;
      rec->last=field;
    }
  else
    {
      rec->first=field;
      rec->last=field;
    }
  field->offsetBeginField=rec->offset;
  field->offsetEndField=rec->offset;
  field->offsetEffectiveEndField=rec->offset;
  field->offsetEffectiveBeginField=rec->offset;
  return field;
}

DamRecord *DamRecord_Create(s,i)
     int s;
     int i;
{
  DamRecord *record;
  record = (DamRecord *)malloc (sizeof(DamRecord));
  record->purged=0;
  record->bufferInput=BufferCreate(s,i);
  record->key        =BufferCreate(100,100);
  record->content    =BufferCreate(100,100);
  record->tag        =NULL;
  record->recordNode=NULL;
  record->first=NULL;
  record->last=NULL;
  record->firstFreeList=NULL;

  return record;
}

DamRecord *DamRecord_Purge(rec)
     DamRecord *rec;
{
  DamRecordField *field;
  if (rec->purged)
    {
      if (rec->recordNode)SgmlFree(rec->recordNode);
      if ((field=rec->first))
	{
	  rec->firstFreeList=field;
	  rec->first=NULL;
          rec->last=NULL;
	}
      rec->recordNode=NULL;
      BufferReset(rec->key);
      rec->purged=0;
    }
  return rec;
}

void DamRecord_Free(rec)
     DamRecord *rec;
{
  DamRecordField *f1;
  DamRecordField *f2;
  if(rec)
    {
      DamRecord_Purge(rec);
      if (rec->key) BufferFree(rec->key);
      if (rec->content) BufferFree(rec->content);
      if (rec->bufferInput) BufferFree(rec->bufferInput);
      if ((f1=rec->firstFreeList))
	{
	  do
	    {
	      f2=f1->next;
	      free (f1);
	    } while(f2);
	}
      free (rec);
    }
}

DamRecord *DamRecord_Init(rec)
     DamRecord *rec;
{
  DamRecord_Purge(rec);
  rec->offset=0;
  rec->purged=1;
  return rec;
}

DamRecord *DamRecord_getUntil(rec, f1, s1)
     DamRecord *rec;
     FILE      *f1;
     char *s1;
{
  char *posStr;
  if (BufferGets(internalBuffer))
    {
      BufferStrcat(rec->bufferInput,"\n");
      if ((posStr=strstr(BufferString(internalBuffer),s1)))
	{
	  rec->offset=
	    BufferLen(rec->bufferInput)
	    +(posStr-BufferString(internalBuffer));
	  BufferStrcat(rec->bufferInput, BufferString(internalBuffer));
	  rec->stringBuf=BufferString(rec->bufferInput);
	  return rec;
	}
      else
	{
	  BufferStrcat(rec->bufferInput, BufferString(internalBuffer));
	  return DamRecord_getUntil(rec, f1, s1);
	}
    }
  else
    {
      /* ExceptSetError("DamRecord","01","string ", s1, "unreachable",1); */
       return 0;
    }
}

DamRecord *      DamRecord_until(rec, f1, s1)
     DamRecord *rec;
     FILE      *f1;
     char *s1;
{
  char *posStr;
  if ((posStr=strstr(BufferString(rec->bufferInput)+rec->offset,s1)))
    {
      rec->offset=posStr-BufferString(rec->bufferInput);
      return rec;
    }
  else return DamRecord_getUntil(rec, f1, s1);  
}

DamRecord *DamRecord_GetSgmlDtd(rec, f1)
     DamRecord *rec;
     FILE      *f1;
/*                             rec->offset -> !  */
{
  DamRecordField *field;
  char *posStr;

  switch(rec->stringBuf[rec->offset+1])
    {
    case '-':               /*  commentaire SGML */
      rec->offset+=3;
      field= DamRecordField_Get(rec);
      field->offsetEffectiveBeginField=rec->offset;
      if (!(DamRecord_until(rec, f1, "-->")))
	{
	  ExceptSetError("DamRecord","01","string ", "-->", "unreachable",1);
	  return 0;
	}
      field->offsetEffectiveEndField=rec->offset;
      if((posStr=strchr(BufferString(rec->bufferInput)+rec->offset, '\t')))
	{
	  field->offsetEndField=posStr-BufferString(rec->bufferInput);
	}
      else field->offsetEndField=BufferLen(rec->bufferInput);
      rec->offset=field->offsetEndField;
      field->type='C';
      break;

    default:

      break;
    }

  return rec;
}

DamRecord *DamRecord_GetSgmlNode(rec, f1)
     DamRecord *rec;
     FILE      *f1;
{
  DamRecordField *field;
  char *afterMark;
  char *beginMark;
  char *endMark;
  char *posTab;
  int lenMark;

  beginMark=BufferString(rec->bufferInput)+rec->offset+1;
  field= DamRecordField_Get(rec);
  if ((afterMark=RegExpAfter(RE_SgmlId,beginMark)))
    {
      lenMark=afterMark-beginMark;
      endMark= malloc(lenMark+4);
      strcpy(endMark,"</");
      strncat(endMark, beginMark, afterMark-beginMark);
      endMark[lenMark+2]='>';
      endMark[lenMark+3]=0;
      field->offsetEffectiveBeginField=rec->offset;
      
      if(afterMark[0]=='>')
	rec->offset+=lenMark+1;
      else
	{
	  char *effectiveAfterMark;
	  if (!(effectiveAfterMark=strchr(afterMark,'>')))
	    ExceptSetError("DamRecord","01","start tag ",beginMark , "not terminated",1);
	  rec->offset+=(effectiveAfterMark-beginMark);
	  afterMark=effectiveAfterMark;
	}
      if (afterMark[-1]=='/')
	{
	  /* empty element record */
	  field->offsetEffectiveEndField=rec->offset+2;
	  
	}
      else
	{
	  /* DamRecord_until(rec, f1, endMark ); */
	  {
	    char *posStr;
	    char c1;
	    
	    rec->pileMark=0;
	    posStr=afterMark;
	    while (posStr++)
	      {
		if((c1=*posStr))
		  {
		    if(c1=='<')
		      {
			if (*(posStr+1)=='/')
			  {
			    if (strncmp(endMark+2,posStr+2,lenMark+1)==0)
			      {
				if (rec->pileMark==0)
				  {
				    rec->offset=posStr-BufferString(rec->bufferInput);
				    break;
				  }
				else rec->pileMark--;
			      }		    
			  }
			else
			  {
			    if (strncmp(endMark+2,posStr+1,lenMark)==0)
			      {
				char cEnd;
				cEnd=*(posStr+1+lenMark);
				if((cEnd=='>')||(cEnd==' '))rec->pileMark++;
			      }
			  }
		      }
		  }
		else
		  {
		    DamRecord_getUntil(rec, f1, endMark);
		    break;
		  }	    
	      }
	  }
	  
	  field->offsetEffectiveEndField=rec->offset+lenMark+3;
	}
      if((posTab=strchr(BufferString(rec->bufferInput)+rec->offset, '\t')))
	{
	  field->offsetEndField=posTab-BufferString(rec->bufferInput);
	}
      else field->offsetEndField=BufferLen(rec->bufferInput);
      rec->offset=field->offsetEndField;
    }
  else ExceptSetError
    ("DamRecord","01","Sgml tag not fit: ",beginMark-1,"" ,1);
  
  field->type='S';
  free(endMark);
  return rec;
}

DamRecord *DamRecord_GetSgml(rec, f1)
     DamRecord *rec;
     FILE      *f1;
{

  switch(rec->stringBuf[rec->offset+1])
    {
    case '!':
      rec->offset++;
      DamRecord_GetSgmlDtd(rec, f1);
      break;
    default:
      DamRecord_GetSgmlNode(rec, f1);
      break;
    }
  return rec;
}

DamRecord *DamRecord_GetDataNULL(rec, f1)
     DamRecord *rec;
     FILE      *f1;
{
  DamRecordField *field;
  field= DamRecordField_Get(rec);
    field->type='D';
  return rec;
}

DamRecord *DamRecord_GetData(rec, f1)
     DamRecord *rec;
     FILE      *f1;
{
  DamRecordField *field;
  char *posTab;
  field= DamRecordField_Get(rec);
  if((posTab=strchr(BufferString(rec->bufferInput)+rec->offset, '\t')))
    {
      field->offsetEndField=posTab-BufferString(rec->bufferInput);
    }
  else
    {
      field->offsetEndField=BufferLen(rec->bufferInput);
    }
  field->offsetEffectiveEndField=field->offsetEndField;
  field->type='D';
  rec->offset=field->offsetEffectiveEndField;
  return rec;
}


DamRecord *DamRecord_NewFieldFromTab(rec,f1)
     DamRecord *rec;
     FILE      *f1;
{

   rec->offset++;

   switch(rec->stringBuf[rec->offset])
     {
     case '\0':
       DamRecord_GetDataNULL(rec,f1);
       break;
     case '<':
       DamRecord_GetSgml(rec, f1);
       if (rec->stringBuf[rec->offset]=='\t')DamRecord_NewFieldFromTab(rec,f1);
       break;
     case ' ':
       break;
     case '\t':
       DamRecord_GetDataNULL(rec,f1);
       DamRecord_NewFieldFromTab(rec,f1);
       break;
     default:
       DamRecord_GetData(rec,f1);
       if (rec->stringBuf[rec->offset]=='\t')DamRecord_NewFieldFromTab(rec,f1);
       break;
     }
   return rec;
}

DamRecord *DamRecord_fgets(rec, f1)
     DamRecord *rec;
     FILE      *f1;
{
  DamRecordField *field;

  DamRecord_Init(rec);
  if ((rec->stringBuf=BufferFgets(rec->bufferInput,f1)))
    { 
     switch(rec->stringBuf[rec->offset])
       {
	case '\0':

	  break;

	case '<':
	  DamRecord_GetSgml(rec, f1);
	  if (rec->stringBuf[rec->offset]=='\t')DamRecord_NewFieldFromTab(rec,f1);
	  break;
	  /*
	case ' ':
           break;
	   */

	case '\t':

	  break;
	default:
	  DamRecord_GetData(rec,f1);
          if (rec->stringBuf[rec->offset]=='\t')DamRecord_NewFieldFromTab(rec,f1);
	  break;
	}

     if ((field=rec->first))
       {
	 do
	   {
	     field->beginField=
	       BufferString(rec->bufferInput)+field->offsetBeginField;
	     field->endField  =
	       BufferString(rec->bufferInput)+field->offsetEndField;
	     field->effectiveBeginField=
	       BufferString(rec->bufferInput)+field->offsetEffectiveBeginField;
	     field->effectiveEndField=
	       BufferString(rec->bufferInput)+field->offsetEffectiveEndField;
	   }
	 while((field=field->next));

	 field=rec->first;
	 if (field->next && (field->type=='D'))
	     {
	       BufferStrncat
		 (rec->key,
		  field->effectiveBeginField,
                  field->effectiveEndField-field->effectiveBeginField);
	     }

       }
     
     return rec;

    }
  else return NULL;
}

char *DamRecordSeekKey(record)
     DamRecord *record;
{
  if(record)
    {
      return BufferString(record->key);
    }
  else return NULL;
}

char *DamRecordAfterKey(record)
     DamRecord *record;
{
  DamRecordField *f;
  if(record)
    {
      if ((f=record->first->next))
	{
	  return f->beginField ;
	}
    }
  return NULL;
}

char *DamRecordGetString(r1,n1)
  DamRecord *r1;
  int n1;
{
  DamRecordField *f1;
  int n2;

  if(r1)
    {
      switch (n1)
	{
	case 0:
	  f1=r1->last;
	  break;
	case 1:
	  f1=r1->first;
	  break;

	default:
	  if((f1=r1->first))
	    {
	      n2=n1-1;
	      while(((n2--)>0))
		{
		  if (!(f1=f1->next))  return NULL;
		}
	    }
	  break;
	 
	}
      if(f1)
	{
	  f1->effectiveEndField[0]='\0';
	  return f1->beginField;	  
	}
    }
  return NULL;

}

void DamRecordPrintStdin(r)
DamRecord *r;
{
  char *line;
  Buffer *b;
  if(r)
    {
      if((b=r->bufferInput))
	{
	  if((line=BufferString(b)))
	    {
	      printf("%s\n",line);
	    }
	}
    }
}
