/*******************************************************
* unit:    lists             release 0.15              *
* purpose: general manipulation with array of strings  *
* Licency: GPL or LGPL                                 *
* Copyright: (c) 1998-2019 Jaroslav Fojtik             *
********************************************************/
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "lists.h"
#include "std_str.h"


//Constructors of class list
list::list(int n)
{
 number=0;
 if(n==0) {pstr=NULL;goto NoData;}
 if( (pstr=(char **)malloc(n*sizeof(char *)))==NULL)
	{
	RaiseError(ListsId|No_Memory,this);	//Memory Exhausted
NoData:	numalloc=0;
	return;
	}
 memset(pstr,0,n*sizeof(char *));
 numalloc=n;
}


list::list(const char *str)
{
 if( (pstr=(char **)malloc(sizeof(char *)))==NULL)
	{
	numalloc=number=0;
	RaiseError(ListsId|No_Memory,this);	//Memory Exhausted
	return;
	}
 numalloc=1;
 if( (*pstr = StrDup(str)) == NULL)
	{
	number=0;
	if(str!=NULL)
	  if(*str!=0) RaiseError(ListsId|No_Memory,this);	//Memory Exhausted
	}
 else number=1;
}


list::list(const list &l)
{
int i;
char **cpstr1,**cpstr2;

if(l.number==0 || l.pstr==NULL)
	{
	numalloc=number=0;
	pstr=NULL;
	return;
	}
if( (cpstr1=pstr=(char **)calloc(sizeof(char *),l.number))==NULL)
	{
	numalloc=number=0;
	RaiseError(ListsId|No_Memory,this);	//Memory Exhausted
	return;
	}
cpstr2=l.pstr;
number=numalloc=l.number;
for(i=0;i<l.number;i++)
	{
	*cpstr1++ = StrDup(*cpstr2++);
	}
}


list::list(const char* const* data, int count)
{
int i;
char **cpstr1;

 if( (cpstr1=pstr=(char **)calloc(sizeof(char *),count))==NULL)
	{
	numalloc=number=0;
	RaiseError(ListsId|No_Memory,this);	//Memory Exhausted
	return;
	}
 number=numalloc=count;
 for(i=0;i<count;i++)
	{
	*cpstr1++ = StrDup(*data++);
	}
};


list::list(temp_list &l)
{
 list *pl=&l;
 number=pl->number;numalloc=pl->numalloc;pstr=pl->pstr;
 pl->number=pl->numalloc=0; pl->pstr=NULL;
}


//------------family of operator = -------------

//Operator = that assigns string into list
list &list::operator=(const char *str)
{
 erase();
 if( (pstr=(char **)calloc(sizeof(char *),1))==NULL)
	{
	numalloc=number=0;
	RaiseError(ListsId|No_Memory,this);	//Memory Exhausted
	return(*this);
	}
 number=numalloc=1;
 *pstr = StrDup(str);

return(*this);
}


//Operator = that makes identical copy of the list
list &list::operator=(const list &l)
{
int i;
char **cpstr1,**cpstr2;

  if(this==&l) return *this;	// check for self assignment

  erase();
  if( (cpstr1=pstr=(char **)calloc(sizeof(char *),l.number))==NULL)
	{
	numalloc=number=0;
	RaiseError(ListsId|No_Memory,this);	//Memory Exhausted
	return(*this);
	}
  number=numalloc=l.number;
  cpstr2=l.pstr;
  for(i=0;i<l.number;i++)
	{
	*cpstr1++ = StrDup(*cpstr2++);
	}

return(*this);
}


list &list::operator=(temp_list &l)
{
list *pl=&l;
  erase();
  number=pl->number;numalloc=pl->numalloc;pstr=pl->pstr;
  pl->number=pl->numalloc=0; pl->pstr=NULL;
return(*this);
}

//------------family of operator + -------------
/*This operator adds one item to the list.*/
int list::operator+=(const char *str)
{
 if(number>=numalloc || pstr==NULL)
	{
        void *cpstr = (char **)realloc(pstr,++numalloc*sizeof(char *));
	if(cpstr==NULL)
		{                
                free(pstr); pstr=NULL;
		number=numalloc=0;
		RaiseError(ListsId|No_Memory,this);	//Memory Exhausted
		return(-1);
		}
        pstr = (char**)cpstr;
	}

 if(str)
   {
   if((pstr[number]=StrDup(str))==NULL)
	{
	RaiseError(ListsId|No_Memory,this);	//Memory Exhausted
	return(-1);
	}
   }
 else pstr[number]=NULL;

return(number++);
}


/*This operator adds one list another list.*/
int list::operator+=(const list & l)
{
int i;
char **cpstr;
int l_number;

l_number=l.number;	//save l.number for the situation when: this==l
if(l.pstr!=NULL && l_number>0)
  {
  if(number+l_number>numalloc || pstr==NULL)
	{
	numalloc=number+l_number;
        cpstr = (char**)realloc(pstr,numalloc*sizeof(char *));
	if(cpstr==NULL)
		{
                free(pstr); pstr=NULL;
		number = numalloc = 0;
		RaiseError(ListsId|No_Memory,this);	//Memory Exhausted
		return(-1);
		}
        pstr = cpstr;       
	}

  cpstr=l.pstr;
  for(i=0;i<l_number;i++)
	  {
	  pstr[number++]=StrDup(*cpstr);
	  cpstr++;
	  }
  }

return(number);
}


temp_list operator+(const list &l1,const list &l2)
{
#ifndef OptSize
int i;
temp_list tmp_list(l1.number+l2.number);

 if(tmp_list.pstr==NULL) return tmp_list;		//Memory exhausted
 for(i=0;i<l1.number;i++)
	tmp_list.pstr[i]=StrDup(l1.pstr[i]);
 for(i=0;i<l2.number;i++)
	tmp_list.pstr[i+l1.number]=StrDup(l2.pstr[i]);
 tmp_list.number=l1.number+l2.number;
#else
  temp_list tmp_list(l1);
  tmp_list+=l2;
#endif
return(tmp_list);
}


temp_list operator+(const list &l1,const char *str)
{
#ifndef OptSize
int i;

  temp_list tmp_list(l1.number+1);
  if(tmp_list.pstr==NULL) return tmp_list;
  for(i=0;i<l1.number;i++)
	tmp_list.pstr[i]=StrDup(l1.pstr[i]);
  tmp_list.pstr[l1.number]=StrDup(str);;
  tmp_list.number=l1.number+1;
#else
  temp_list tmp_list(l1);
  tmp_list+=str;
#endif
return(tmp_list);
}


temp_list operator+(const char *str,const list &l2)
{
#ifndef OptSize
int i;

  temp_list tmp_list(l2.number+1);
  if(tmp_list.pstr==NULL) return tmp_list;
  tmp_list.pstr[0]=StrDup(str);
  for(i=0;i<l2.number;i++)
	tmp_list.pstr[i+1]=StrDup(l2.pstr[i]);
  tmp_list.number=l2.number+1;
#else
  temp_list tmp_list(str);
  tmp_list+=l2;
#endif
return(tmp_list);
}



//------------family of operator - -------------


temp_list operator-(const list &l1, const char *str)
{
int pos;
temp_list tmp_list(l1);

  if((pos = (str IN l1))!=0)
    {
    pos--;
    if(tmp_list.pstr[pos])
      {
      free(tmp_list.pstr[pos]);
      tmp_list.pstr[pos] = NULL;
      }
      
    }

return(tmp_list);
}


temp_list operator-(const list &l1, const list &l2)
{
int i,pos;
temp_list tmp_list(l1);

  for(i=0;i<l2.number;i++)
    {
    if((pos = (l2[i] IN l1))!=0)
      {
      pos--;
      if(tmp_list.pstr[pos])
        {
	free(tmp_list.pstr[pos]);
	tmp_list.pstr[pos] = NULL;
        }
      }
      
    }

return(tmp_list);
}



//------------family of operator | -------------
/*This operator adds one item to the list only if it is not present yet.*/
int list::operator|=(const char *str)
{
int i;

if((i=(str IN *this))>0) return(i-1);

#ifndef OptSize
 if(number>=numalloc || pstr==NULL)
	{
        void *cpstr = realloc(pstr,++numalloc*sizeof(char *));
	if(cpstr==NULL)
		{
                free(pstr); pstr=NULL;
		number=numalloc=0;
		RaiseError(ListsId|No_Memory,this);	//Memory Exhausted
		return(-1);
		}
        pstr = (char**)cpstr;
	}

 if(str!=NULL)
   {
   if( (pstr[number]=strdup(str))==NULL )
     {
     RaiseError(ListsId|No_Memory,this);	//Memory Exhausted
     return(-1);
     }
   }
 else pstr[number]=NULL;

 return(number++);
#else
 tmp_list+=str;
 return(number);
#endif
}


int list::operator|=(const list & l)
{
int i;
char **cpstr;

cpstr=l.pstr;
if(cpstr!=NULL && l.number>0)
  {
  cpstr=l.pstr;
  for(i=0;i<l.number;i++)
  	  {
	  if(*cpstr!=NULL) *this|=*cpstr;
	  cpstr++;
	  }
  }
return(number);
}


temp_list operator|(const list &l1,const list &l2)
{
temp_list tmp_list(l1);
  tmp_list|=l2;
return(tmp_list);
}


temp_list operator|(const list &l1,const char *str)
{
temp_list tmp_list(l1);
  tmp_list|=str;
return(tmp_list);
}


//This function compare two lists
int ListCmp(const list &l1,const list &l2)
{
int j,minnumber;
char **cpstr1,**cpstr2;

 cpstr1=l1.pstr;
 cpstr2=l2.pstr;

 minnumber=(l1.number<l2.number?l1.number:l2.number);
 while(minnumber-->0)
	{
	if(j=StrCmp(*cpstr1++,*cpstr2++)) return(j);
	}
 if(l1.number<l2.number) return -1;
 if(l1.number>l2.number) return 1;
return(0);
}


/*Operators IN check whether string is element of the given list*/
int list::operator IN(const char *str) const
{
int i;
char **cpstr;


if(pstr==NULL)
	{
	if(str==NULL) return(-1);
	if(*str==0) return(-1);
	return(0);
	}
cpstr=pstr;
for(i=0;i<number;i++)
	{
	if(!strcmp(str,*cpstr++)) return(i);
	}
return(-1);
}

int operator IN(const char *str,const list &l)
{
int i;
char **cpstr;

 cpstr=l.pstr;
 for(i=0;i<l.number;i++)
	{
	if(!StrCmp(str,*cpstr++)) return(i+1);
	}
 return(0);
}


int operator IN(const list &l1,const list &l2)
{
int i;

  for(i=0;i<l1.number;i++)
	{
	if(!(l1[i] IN l2)) return(0);
	}

return(1);
}


/*This function includes one string into list without allocation - string must be allocated before*/
int MoveSTR(list & l, char *str)
{
 if(l.number>=l.numalloc || l.pstr==NULL)
	{
        void *cpstr = realloc(l.pstr,++l.numalloc*sizeof(char *));
	if(cpstr==NULL)
		{
		l.number=l.numalloc=0;
		RaiseError(ListsId|No_Memory,&l);	//Memory Exhausted
		free(str);				//toss str
		return(-1);
		}
        l.pstr = (char **)cpstr;
	}
 l.pstr[l.number]=str;

return(l.number++);
}


/*This function includes one string into list without allocation - string must be allocated before*/
int MoveSTRPos(list & l, char *str, int Pos)
{
 if(Pos>=l.numalloc || l.pstr==NULL)
	{
	l.numalloc=Pos+1;
        void *cpstr = realloc(l.pstr,l.numalloc*sizeof(char *));
	if(cpstr==NULL)
		{
                free(l.pstr); l.pstr=NULL;
		l.number=l.numalloc=0;
		RaiseError(ListsId|No_Memory,&l);	//Memory Exhausted
		free(str);				//toss str
		return(-1);
		}
        l.pstr = (char**)cpstr;
	}
 if(Pos>=l.number)
	{
	memset(l.pstr+l.number,0,(Pos-l.number)*sizeof(char *));
	l.number=Pos+1;
	}
 else if(l.pstr[Pos]!=NULL) free(l.pstr[Pos]);

 l.pstr[Pos]=str;

return(l.number);
}



//------ miscellaneous functions for manipulation with list ---------
void list::erase(void)
{
int i;
char **cpstr;

  if( (cpstr=pstr) == NULL ) return;
  for(i=0;i<number;i++)
	{
	if(*cpstr!=NULL) free(*cpstr);
	cpstr++;
	}
  free(pstr);

  pstr=NULL;
  numalloc=number=0;
}


int list::check(void)
{
int i,ret;
char **cpstr;

ret=0;
if(pstr==NULL)
      {
      if(numalloc!=0) {numalloc=0;  ret=ListsId | Bad_Size;}
      if(number!=0)   {number=0;    ret=ListsId | Bad_Number;}
      return(ret);
      }

if(numalloc==0)
	{
	number=0;
	pstr=NULL;
	return(ListsId | Bad_Size);
	}

if(number>numalloc)
	{
	number=numalloc;
	ret=ListsId | Bad_Number;
	}

cpstr=pstr;
for(i=0;i<number;i++)
	{
        if(*cpstr==NULL) ret=ListsId | Bad_Item;
        cpstr++;
	}

return(ret);
}


int AddSTRSorted(list & l,TSorter sorter, const char *str, int AllowDupl)
{
int i,q;
char **cpstr,*dupstr;

if(str==NULL) return(0);
if(*str==0) return(0);

cpstr=l.pstr;
i=0;

if(sorter!=NULL)
  while(i<l.number)
	{
	q = sorter(str,*cpstr);
	if(q==0 && !AllowDupl) return(i);
	if(q<=0) break;
	i++;
	cpstr++;
	}
else i=l.number;

if( (cpstr=(char **)calloc(sizeof(char *),l.number+1))==NULL)
	{
	RaiseError(ListsId|No_Memory,&l);	//Memory Exhausted
	return(-1);
	}
if((dupstr=strdup(str))==NULL)
	{
	free(cpstr);
	RaiseError(ListsId|No_Memory,&l);	//Memory Exhausted
	return(-1);
	}

if(l.number==0 || l.pstr==NULL)
   {
   *cpstr=dupstr;
   }
else {
     memmove(cpstr,l.pstr,i*sizeof(char *));
     cpstr[i]=dupstr;
     memmove(cpstr+i+1,l.pstr+i,(l.number-i)*sizeof(char *));
     free(l.pstr);
     }

l.pstr=cpstr;
l.numalloc=++l.number;

return(i);
}


////////////// Sorted Lists + Sorters etc. //////////////

inline void split(TSorter sorter, char **item, int d, int h, int &i, int &j)
{
char *pivot,*temp;

i=h;j=d;
pivot=item[(d+h)/2];

do {
   while(sorter(item[j],pivot)<0) j++;
   while(sorter(item[i],pivot)>0) i--;

   if(i>=j)
     {
     temp=item[i];
     item[i]=item[j];
     item[j]=temp;
     i--;
     j++;
     }
   } while(j<i);
}

void Qsort(TSorter sorter, char **item, int d, int h)
{
int i,j;

split(sorter,item,d,h,i,j);
if(d<i) Qsort(sorter,item,d,i);
if(h>j) Qsort(sorter,item,j,h);
}


int operator IN(const char *str,const sortedlist &l)
{
int hi,lo,pivot,s;

 if(l.pstr==NULL)
	{
	if(str==NULL) return(-1);
	if(*str==0) return(-1);
	return(0);
	}
 if(l.sorter==NULL) return(str IN (list)l);
 lo=0;
 hi=l.number-1;

 while(hi>=lo)
	{
	pivot=(lo+hi)/2;
	s=l.sorter(str,l.pstr[pivot]);
	if(s>0)
	   {
	   lo=pivot+1;
	   continue;
	   }
	if(s==0) return(pivot+1);
	if(s<0)
	   {
	   hi=pivot-1;
	   continue;
	   }
	}

 return(0);
}


/*This procedure removes all duplicated items from sorted list*/
int RemoveDups(sortedlist & l)
{
int i,r;
char **apstr,**bpstr;

if(l.pstr==NULL || l.number<=1) return(0);
r=0;
apstr=bpstr=l.pstr;
for(i=0;i<l.number-1;i++)
	{
	if(*apstr==NULL)
		{
		r++;
		apstr++;
		continue;
		}
	if(apstr[1]!=NULL)
	    if(!strcmp(*apstr,apstr[1]))
		{
		r++;
		free(*apstr);
		*apstr=NULL;
		apstr++;
		continue;
		}
	if(*bpstr==NULL)
		  {
		  *bpstr=*apstr;
		  *apstr=NULL;
		  }
	if(*bpstr!=NULL) bpstr++;
	apstr++;
	}
if(*bpstr==NULL)
    {
    *bpstr=*apstr;
    *apstr=NULL;
    }

l.number-=r;
return(r);
}


#ifdef Streams
ostream &operator<<(ostream &xout, const list &l)
{
int i;

  xout<<"{";
  for(i=0;i<length(l);i++)
	{
	if(i>0) xout<<",";
	xout<<"\""<<(l[i]==NULL?"NULL":l[i])<<"\"";
	}
  xout<<"}";
return xout;
}

#endif
