/**********************************************************************
*
*  projet   : DilibPro
*  module   : SgmlPath
*  commande : SgmlSelect
*  fichier  : SgmlSelect.c
*  Auteur   : Jacques DUCLOY
*  Date     : Octobre 93
*  $Id: SgmlSelect.c,v 1.3 2006/07/06 13:12:35 parmentf Exp $
*
***********************************************************************/

#include <stdlib.h>
#include "ctype.h"
#include "DamFile.h"
#include "SgmlPath.h"
#include "Buffer.h"

static int flagLine;

#ifdef DILIBDEBUG
    static int ModeTrace=0;
#endif

#define DamToolListPrevNextInsert(list,elem)                   \
{                                                       \
       if (list->first)                                 \
         {                                              \
           elem->prev=list->last;                       \
           list->last->next=elem;                       \
         }                                              \
       else                                             \
         {                                              \
           elem->prev=NULL;                             \
           list->first=elem;                            \
         }                                              \
       list->last=elem;                                 \
       elem->next=NULL;                                 \
}

void DamSgmlSelect_Error();
void computeSelectZone();
/************************************************************************
 
      DECLARATION DES STRUCTURES ET COMPILATIONS

************************************************************************/

/******************* Les variables **************************************/
SgmlNode **tabVarGrep;
SgmlNode **tabVarSplit;
SgmlNode **tabVarSplitSorted;
int      numberOfVarGrep;
int      numberOfVarSplit;
int      flagKeepDoublon;

/************************************************************************/
struct printZone
{
  char type;
                   /* 
		     C : string (constant)
		     n : node number
		     a : sgml variable
		     s : string variable
		    */
  char varType;
                   /*
		     g : grep
		     s : split
                     a : sorted split (assoc)
		     */
  char *string;
  int number;
  struct printZone *prev;
  struct printZone *next;
};

struct listPrintZone
{
  struct printZone *first;
  struct printZone *last;
};

void compilePrintZone (listPrint, str)
     struct listPrintZone *listPrint;
     char *str;
{
  int lengthStr;
  struct printZone *zone;
  char *nextToken;
  char *afterNumber;
  int   numberLength = 0;
  if(str&&(lengthStr=strlen(str)>0))
    {
      zone=(struct printZone *)malloc(sizeof(struct printZone));
      DamToolListPrevNextInsert(listPrint, zone);
      if((nextToken=strpbrk(str,"@${\\")))
	 {
	   if(str==nextToken)
	     {
	       switch(*str)
		 {
		 case '@':
		   if (isdigit((int)str[1]))
		     {
		       numberLength = 1;
		       zone->type='n';
		       zone->number=atoi(str+1);
		       if(zone->number)
			 while(isdigit((int)str[numberLength])) numberLength++;
		       else
			 numberLength = 2;
		       compilePrintZone(listPrint,str+numberLength);
		     }
		   else
		     {
		       switch (str[1])
			 {
			 case 'G':
			   zone->type='a';
			   zone->varType='g';
			   zone->number=numberOfVarGrep;
			   compilePrintZone(listPrint,str+2);
			   break;
			 case 'S':
			   zone->type='a';
			   zone->varType='s';
			   zone->number=numberOfVarSplit;
			   compilePrintZone(listPrint,str+2);
			   break;
			 case 'A':
			   zone->type='a';
			   zone->varType='a';
			   zone->number=numberOfVarSplit;
			   compilePrintZone(listPrint,str+2);
			   break;

			 case 'g':			   
			   zone->type='a';
			   zone->varType='g';
			   zone->number=atoi(str+2);
			   afterNumber=str+3;
			   while(isdigit((int)*afterNumber))afterNumber++;
			   compilePrintZone(listPrint,afterNumber);
			   break;
			 case 's':			   
			   zone->type='a';
			   zone->varType='s';
			   zone->number=atoi(str+2);
			   afterNumber=str+3;
			   while(isdigit((int)*afterNumber))afterNumber++;
			   compilePrintZone(listPrint,afterNumber);
			   break;
			 case 'a':			   
			   zone->type='a';
			   zone->varType='a';
			   zone->number=atoi(str+2);
			   afterNumber=str+3;
			   while(isdigit((int)*afterNumber))afterNumber++;
			   compilePrintZone(listPrint,afterNumber);
			   break;
			 case '@':
			   zone->type='C';
			   zone->string="@";
			   compilePrintZone(listPrint,str+2);
			   break;
			 default:
			   DamSgmlSelect_Error(str, ": unknowned variable");
			 }
		     }
		   break;

		 case '$':
		   switch (str[1])
		     {
		     case 'G':
		       zone->type='s';
		       zone->varType='g';
		       zone->number=numberOfVarGrep;
		       compilePrintZone(listPrint,str+2);
		       break;			
		     case 'S':
		       zone->type='s';
		       zone->varType='s';
		       zone->number=numberOfVarSplit;
		       compilePrintZone(listPrint,str+2);
		       break;
		     case 'A':
		       zone->type='s';
		       zone->varType='a';
		       zone->number=numberOfVarSplit;
		       compilePrintZone(listPrint,str+2);
		       break;
		     case 'g':			   
		       zone->type='s';
		       zone->varType='g';
		       zone->number=atoi(str+2);
		       compilePrintZone(listPrint,str+3);
		       break;
		     case 's':			   
		       zone->type='s';
		       zone->varType='s';
		       zone->number=atoi(str+2);
		       compilePrintZone(listPrint,str+3);
		       break;
		     case 'a':			   
		       zone->type='s';
		       zone->varType='a';
		       zone->number=atoi(str+2);
		       compilePrintZone(listPrint,str+3);
		       break;

		     default:
		       DamSgmlSelect_Error(str, ": unknowed variable");
		     }

		 case '\\':
		   switch (str[1])
		     {
		     case 'n':
		       zone->type='C';
		       zone->string="\n";
		       compilePrintZone(listPrint,str+2);
		       break;
		     case 't':
		       zone->type='C';
		       zone->string="\t";
		       compilePrintZone(listPrint,str+2);
		       break;
		     default:
		       zone->type='C';
		       zone->string=(char *)malloc(2 *sizeof(char));
		       zone->string[1]='\0';
		       zone->string[0]=str[1];
		       compilePrintZone(listPrint,str+2);
		       break;		       
		     }
		 }
	     }
	   else
	     {
	       zone->type='C';
	       zone->string=malloc(nextToken-str+1);
	       zone->string[0]='\0';
	       strncat(zone->string, str, nextToken-str);
	       compilePrintZone(listPrint,nextToken);
	     }
	 }
      else
	 {
	   zone->type='C';
	   zone->string=str;
	 }
    }
}
/***********************************************************************/
struct grepSplitSubCommand
{
  SgmlPathProg     *prog;
  SgmlPathIterator *iter;
  struct grepSplitSubCommand  *prev;
  struct grepSplitSubCommand  *next;
  struct grepSplitSubCommand  *nextAssoc;
};

struct listGrepSplitSubCommand
{
  struct grepSplitSubCommand *first;
  struct grepSplitSubCommand *last;
  int                         zoneNumber;
};

struct grepSplitSubCommand *grepSplitSubCommandCreate(str)
     char *str;
{
  struct grepSplitSubCommand *zone;
  zone = (struct grepSplitSubCommand *)
    malloc(sizeof(struct grepSplitSubCommand ));
  zone->prog=SgmlPathCompile(str);
#ifdef DILIBDEBUG
  if (ModeTrace) SgmlPathProgVisual(zone->prog);
#endif
  zone->iter=SgmlPathIteratorCreate(zone->prog);
  return zone;
}

struct grepSplitSubCommand *grepSplitSubCommandClone(zone1)
  struct grepSplitSubCommand *zone1;

{
  struct grepSplitSubCommand *zone2;
  zone2 = (struct grepSplitSubCommand *)
    malloc(sizeof(struct grepSplitSubCommand ));
  zone2->prog=zone1->prog;
  zone2->iter=SgmlPathIteratorCreate(zone2->prog);
  zone1->nextAssoc=zone2;
  return zone2;
}
/************************************************************************/
     
/************************************************************************/

struct selectZone
{
  char type; 
                           /* 
                              E: Extract (Grep variation)
                              G: Grep
                              S: Split
			      P: Print
			      A: Assoc
			      R: Replace
                              V: inVert grep
                              W: All Print
                           */

  struct selectZone                     *prev;
  struct selectZone                     *next;
  struct selectZone                     *prevSplit;   /* for Assoc */
  struct listPrintZone                  *listPrint;
  struct listGrepSplitSubCommand        *listGrep;
  struct listGrepSplitSubCommand        *listSplit;
  struct grepSplitSubCommand            *splitIterator;
};

struct listZ
{
  struct selectZone *first;
  struct selectZone *last;
};

struct listZ *listZone;

/***********************************************************************/
struct selectZone *selectZoneCreate(c)
     char c;
{
  struct selectZone * zone;
  zone =(struct selectZone *) malloc(sizeof(struct selectZone));
  zone->type=c;

  switch(c)
    {
    case 'P':
      zone->listPrint=
	(struct listPrintZone *)malloc(sizeof(struct listPrintZone));
      zone->listPrint->first=NULL;
      zone->listPrint->last =NULL;
      break;
    case 'G':
    case 'V':
    case 'E':
      zone->listGrep=
	(struct listGrepSplitSubCommand *)malloc
	(sizeof(struct listGrepSplitSubCommand));
      zone->listGrep->first=NULL;
      zone->listGrep->last =NULL;
      break;
    case 'S':
    case 'A':
      zone->listSplit=
	(struct listGrepSplitSubCommand *)malloc
	(sizeof(struct listGrepSplitSubCommand));
      zone->listSplit->first=NULL;
      zone->listSplit->last =NULL;
      break;
    }
  return zone;
}

void selectZoneCloneForAssoc(zoneAssoc, zoneSplit)
  struct selectZone *zoneAssoc;
  struct selectZone *zoneSplit;
{
  struct grepSplitSubCommand  *elemSplit;
  struct grepSplitSubCommand  *elemAssoc;
  if((elemSplit=zoneSplit->listSplit->first))
    {
      do
	{
	  elemAssoc=grepSplitSubCommandClone(elemSplit);
	  DamToolListPrevNextInsert(zoneAssoc->listSplit, elemAssoc);
	}
      while((elemSplit=elemSplit->next));
    }
  zoneAssoc->prevSplit=zoneSplit;
}

/************************************************************************
 
      INTERPRETATION ET EXECUTION

************************************************************************/
int flagPrintTag;
static int flagEcho;

void printPrintZone(zone)
     struct printZone *zone;
{
  SgmlNode *varNode;
  int n1;

  switch(zone->type)
    {
    case 'C':
      printf("%s",zone->string);
      break;
    case 'n':
      if (((n1=zone->number)>0))
	{
          SgmlNode *sonN1;
	  if((sonN1=SgmlGetSonNumber(SgmlInputRecord,n1)))
	    SgmlPrint(sonN1);
	}
      else SgmlPrint(SgmlLast(SgmlInputRecord));
      break;
    case 'a':
      switch(zone->varType)
	{
	case 'g':
	  if ((n1=zone->number))n1=n1-1;
	  else n1=numberOfVarGrep-1;
	  if((varNode=tabVarGrep[n1]))SgmlPrint(varNode);
	  break;
	case 's':
	  if ((n1=zone->number))n1=n1-1;
	  else n1=numberOfVarSplit-1;
	  if((varNode=tabVarSplit[n1]))SgmlPrint(varNode);
	  break;
	case 'a':
	  if ((n1=zone->number))n1=n1-1;
	  else n1=numberOfVarSplit-1;
	  if((varNode=tabVarSplitSorted[n1]))SgmlPrint(varNode);
	  break;
	}
      break;
    case 's':
      switch(zone->varType)
	{
	case 'g':
	  if ((n1=zone->number))n1=n1-1;
	  else n1=numberOfVarGrep-1;
	  if((varNode=tabVarGrep[n1]))printf("%s",SgmlGetFirstData(varNode));
	  break;
	case 's':
	  if ((n1=zone->number))n1=n1-1;
	  else n1=numberOfVarSplit-1;
	  if((varNode=tabVarSplit[n1]))printf("%s",SgmlGetFirstData(varNode));
	  break;
	case 'a':
	  if ((n1=zone->number))n1=n1-1;
	  else n1=numberOfVarSplit-1;
	  if((varNode=tabVarSplitSorted[n1]))printf("%s",SgmlGetFirstData(varNode));
	  break;
	}
      break;
    }
}

SgmlNode *evalGrep(zone)
     struct grepSplitSubCommand *zone;
{
  SgmlNode *resultat;

   SgmlPathIteratorInit(zone->iter,SgmlInputRecord);
   if ((resultat=SgmlPathNext(zone->iter)))
     {
       return resultat;
     }
   else
     {
       if(zone->next)return (evalGrep(zone->next));
       else return NULL;
     }
}

void splitIteratorInit(zone)
     struct selectZone *zone;
{
  zone->splitIterator=zone->listSplit->first;
  SgmlPathIteratorInit(zone->splitIterator->iter,SgmlInputRecord);
  return;
}

void assocIteratorInit(zone)
     struct selectZone *zone;
{
  zone->splitIterator=zone->prevSplit->splitIterator->nextAssoc;
  SgmlPathIteratorCopyContext
    (zone->splitIterator->iter,
     zone->prevSplit->splitIterator->iter);
  return;
}

SgmlNode *splitIteratorNext(zone)
     struct selectZone *zone;
{
  SgmlNode *nextNode;
  if(zone->splitIterator)
    {
      if ((nextNode=SgmlPathNext(zone->splitIterator->iter))) return nextNode;
      while ((zone->splitIterator=zone->splitIterator->next))
	{
	  SgmlPathIteratorInit(zone->splitIterator->iter,SgmlInputRecord);
	  if ((nextNode=SgmlPathNext(zone->splitIterator->iter))) 
	    return nextNode;
	}
    }
  return NULL;
}


void printSelectZone(zone)
     struct selectZone *zone;
{
  struct printZone *element;

  switch(zone->type)
    {
    case 'P':
      if ((flagPrintTag==0))
	flagPrintTag=1;
      else putchar('\t');
      if((element=zone->listPrint->first))
	{
	  do
	    {
	      printPrintZone(element);
	    }
	  while((element=element->next));
	}
      break;

    case 'W':
      if (flagPrintTag==0)
	flagPrintTag=1;
      else putchar('\t');
      SgmlDo(SgmlInputRecord, localField,
	     {
	       SgmlPrint(localField);
	       if(localField->next)putchar ('\t');
	     });
      break;
    default:
      break;
    }

  if(zone->next)printSelectZone(zone->next);
  else
    {
      if(flagPrintTag==1)
	{
	  flagPrintTag=0;
	  if (!flagLine)putchar('\n');
	}
    }
}

int flagHasDoublon;

void insertSortedVarSplit(n1,i)
     SgmlNode *n1;
     int i;
{
  int c1;
  if(i<=0)
    {
      tabVarSplitSorted[0]=n1;
      return;
    }
  if(((c1=SgmlCmp(n1,tabVarSplitSorted[i-1]))>=0))
    {
      if(c1==0)flagHasDoublon=1;
      tabVarSplitSorted[i]=n1;
      return;
    }
  else
    {
      tabVarSplitSorted[i]=tabVarSplitSorted[i-1];
      insertSortedVarSplit(n1,i-1);
      return;
    }
  
}

void execPrint(zone)
     struct selectZone *zone;
{
  if (zone->next) computeSelectZone(zone->next);
  else
    {
      if(numberOfVarSplit)
	{
	  int i;
	  flagHasDoublon=0;
	  tabVarSplitSorted[0]=tabVarSplit[0];
	  i=0;
	  while(++i<numberOfVarSplit)
	    {
	      insertSortedVarSplit(tabVarSplit[i],i);
	    }
	}
      if (flagHasDoublon&&(!flagKeepDoublon))return;
      printSelectZone(listZone->first);
    }
}

void computeSelectZone(zone)
     struct selectZone *zone;
{
  SgmlNode *resNode;

    switch(zone->type)
    {
    case 'G':
      if ((resNode=evalGrep(zone->listGrep->first)))
	{
	  tabVarGrep[zone->listGrep->zoneNumber-1]=resNode;
	  execPrint(zone);
	}
      else if (flagEcho){SgmlPrint(SgmlInputRecord);}
      return;    

    case 'V':
      if ((resNode=evalGrep(zone->listGrep->first)))
	{
	  if (flagEcho){SgmlPrint(SgmlInputRecord);}
	  return;
	}
      else
	{
	  tabVarGrep[zone->listGrep->zoneNumber-1]=NULL;
	  execPrint(zone);
	}
      return;

    case 'E':
      resNode=evalGrep(zone->listGrep->first);
      tabVarGrep[zone->listGrep->zoneNumber-1]=resNode;
      execPrint(zone);
      return;

    case 'W':
    case 'P':
      execPrint(zone);
      return;
    case 'S':
      splitIteratorInit(zone);
      if ((resNode=splitIteratorNext(zone)))
	{
	  do
	    {
	      tabVarSplit[zone->listSplit->zoneNumber-1]=resNode;
	      execPrint(zone);
	    }while((resNode=splitIteratorNext(zone)));
	}
      else if (flagEcho){SgmlPrint(SgmlInputRecord);}
      return;

    case 'A':
      assocIteratorInit(zone);
      while ((resNode=splitIteratorNext(zone)))
	{
	  tabVarSplit[zone->listSplit->zoneNumber-1]=resNode;
	  execPrint(zone);
	}
      return;
    default:
      return;
    }
}


/***************************** main ***********************************/
void usage()
{
  fprintf (stderr,"usage: SgmlSelect [-k] [-s path][-a] [-g path [path...]] [-p printSpec] [-P]\n");
  exit(1);
}

void DamSgmlSelect_Error(mess1, mess2)
     char *mess1;
     char *mess2;
{
  fprintf(stderr, "DamSgmlSelect: *** ERROR ***\n");
  fprintf(stderr, "%s%s\n", mess1, mess2);
  usage();
}

    int getopt();
    extern char *optarg;
    extern int optind;


int main(argc,argv)
     int argc;
     char **argv;
{
  int cod_arg;

  char *nextOptArg;
  Buffer *bufCom;
  char *posPipe;
  struct selectZone             *zone;
  struct selectZone             *lastSplitZone;
  struct grepSplitSubCommand    *elemGrep;
  struct grepSplitSubCommand    *elemSplit;
  int    flagOptPrint;
  int    flagHasAssoc;
  int    flagInvertDoublonMode;

   listZone = (struct listZ*)malloc(sizeof(struct listZ));
  listZone->first=NULL;
  listZone->last =NULL;
  bufCom=BufferCreate(100,10);

  numberOfVarGrep=0;
  numberOfVarSplit=0;
  flagOptPrint=0;
  lastSplitZone=NULL;
  flagKeepDoublon=0;
  flagHasAssoc=0;
  flagInvertDoublonMode=0;
  flagEcho=0;
  flagLine=0;
 
  while ((cod_arg = getopt(argc,argv,"dae:lg:p:s:tv:EP"))!=EOF)
    {
      switch(cod_arg) 
	{
	case 'd':
	  flagInvertDoublonMode=1;
	  break;
	case 'l':
	  flagLine=1;
	  break;

	case 'g':
	case 'e':
	case 'v':
	  zone=selectZoneCreate(toupper(cod_arg));
	  numberOfVarGrep++;
	  DamToolListPrevNextInsert(listZone, zone);
	  elemGrep=grepSplitSubCommandCreate(optarg);
	  DamToolListPrevNextInsert(zone->listGrep, elemGrep);
	  while(optind<=argc)
	    {
	      nextOptArg=argv[optind];
	      if(!nextOptArg)break;
	      if(nextOptArg[0]=='-')break;
	      else
		{
		  optind++;
		  elemGrep=grepSplitSubCommandCreate(nextOptArg);
		  DamToolListPrevNextInsert(zone->listGrep, elemGrep);
		}
	    }
	  zone->listGrep->zoneNumber=numberOfVarGrep;
	  break;

	case 'p':
	  zone=selectZoneCreate('P');
	  DamToolListPrevNextInsert(listZone, zone);
	  compilePrintZone(zone->listPrint, optarg);
	  flagOptPrint=1;
	  break;

	case 's':
	  zone=selectZoneCreate('S');
	  numberOfVarSplit++;
	  DamToolListPrevNextInsert(listZone, zone);
	  if((posPipe=strchr(optarg,'|')))
	    {
	      char *newPos;

	      newPos=optarg;
	      while((posPipe=strchr(newPos,'|')))
		{
		  BufferStrncpy(bufCom, newPos, posPipe-optarg);
		  elemSplit=grepSplitSubCommandCreate
		    (BufferString(bufCom));
		  DamToolListPrevNextInsert(zone->listSplit, elemSplit);
		  newPos=posPipe+1;
		}
	      elemSplit=grepSplitSubCommandCreate(newPos);
	      DamToolListPrevNextInsert(zone->listSplit, elemSplit);
	    }
	  else
	    {
	      elemSplit=grepSplitSubCommandCreate(optarg);
	      DamToolListPrevNextInsert(zone->listSplit, elemSplit);
	      while(optind<=argc)
		{
		  nextOptArg=argv[optind];
		  if(!nextOptArg)break;
		  if(nextOptArg[0]=='-')break;
		  else
		    {
		      optind++;
		      elemSplit=grepSplitSubCommandCreate(nextOptArg);
		      DamToolListPrevNextInsert(zone->listSplit, elemSplit);
		    }
		}
	    }
	  zone->listSplit->zoneNumber=numberOfVarSplit;
	  lastSplitZone=zone;
	  break;

	case 'a':
	  flagHasAssoc=1;
	  zone=selectZoneCreate('A');
	  numberOfVarSplit++;
	  DamToolListPrevNextInsert(listZone, zone);
	  selectZoneCloneForAssoc(zone, lastSplitZone);
	  zone->listSplit->zoneNumber=numberOfVarSplit;
	  lastSplitZone=zone;
	  break;

	case 'P':
	  zone=selectZoneCreate('W');
	  DamToolListPrevNextInsert(listZone, zone);
	  flagOptPrint=1;
	  break;

	case 'E':
	  flagEcho=1;
	  break;

#ifdef DILIBDEBUG
	case 't':
	  ModeTrace=1;
	  {
	    int i;
	    i=0;
	    while(i++<=argc)printf("[%d]:%s\n",i, argv[i]);
	  }
	  break;
#endif

	default:
	  usage();
	}
    }

  if(flagOptPrint==0)
    {
      zone=selectZoneCreate('W');
      DamToolListPrevNextInsert(listZone, zone);
    }

  if (numberOfVarGrep)
    {
      tabVarGrep=(SgmlNode**)malloc(numberOfVarGrep * sizeof(SgmlNode*));
    };

  if (numberOfVarSplit)
    {
      tabVarSplit=(SgmlNode**)malloc(numberOfVarSplit * sizeof(SgmlNode*));
      tabVarSplitSorted=(SgmlNode**)malloc(numberOfVarSplit * sizeof(SgmlNode*));
    };

  if(flagHasAssoc)
    {
      if(flagInvertDoublonMode)flagKeepDoublon=1;
      else flagKeepDoublon=0;
    }
  else
    {
      if(flagInvertDoublonMode)flagKeepDoublon=0;
      else flagKeepDoublon=1;
    };
  while(SgmlInputNextRecord())
    {
      flagPrintTag=0;
      computeSelectZone(listZone->first);
    }
  return 0;
}
