/*******************************************************
* unit:    lists             release 0.20              *
* purpose: general manipulation with array of strings  *
* Licency: GPL or LGPL                                 *
* Copyright: (c) 1998-2025 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)
{
 NumUsed=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=NumUsed=0;
	RaiseError(ListsId|No_Memory,this);	//Memory Exhausted
	return;
	}
 NumAlloc=1;
 if( (*pstr = StrDup(str)) == NULL)
	{
	NumUsed=0;
	if(str!=NULL)
	  if(*str!=0) RaiseError(ListsId|No_Memory,this);	//Memory Exhausted
	}
 else NumUsed=1;
}


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

if(l.NumUsed==0 || l.pstr==NULL)
	{
	NumAlloc=NumUsed=0;
	pstr=NULL;
	return;
	}
if( (cpstr1=pstr=(char **)calloc(sizeof(char *),l.NumUsed))==NULL)
	{
	NumAlloc=NumUsed=0;
	RaiseError(ListsId|No_Memory,this);	//Memory Exhausted
	return;
	}
cpstr2=l.pstr;
NumUsed=NumAlloc=l.NumUsed;
for(i=0;i<l.NumUsed;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=NumUsed=0;
	RaiseError(ListsId|No_Memory,this);	//Memory Exhausted
	return;
	}
 NumUsed=NumAlloc=count;
 for(i=0;i<count;i++)
	{
	*cpstr1++ = StrDup(*data++);
	}
};


list::list(temp_list &l)
{
 list *pl=&l;
 NumUsed=pl->NumUsed; NumAlloc=pl->NumAlloc; pstr=pl->pstr;
 pl->NumUsed=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=NumUsed=0;
	RaiseError(ListsId|No_Memory,this);	//Memory Exhausted
	return(*this);
	}
 NumUsed=NumAlloc=1;
 *pstr = StrDup(str);

return(*this);
}


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

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

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

return(*this);
}


list &list::operator=(temp_list &l)
{
list *pl=&l;
  erase();
  NumUsed=pl->NumUsed;NumAlloc=pl->NumAlloc;pstr=pl->pstr;
  pl->NumUsed=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(pstr==NULL)		// The list is empty.
    {
    *this = str;
    return NumUsed-1;	// returns 0, on alloc failure returns -1
    }

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

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

return(NumUsed++);
}


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

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

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

return(NumUsed);
}


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

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


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

  temp_list tmp_list(l1.NumUsed+1);
  if(tmp_list.pstr==NULL) return tmp_list;
  for(i=0; i<l1.NumUsed; i++)
	tmp_list.pstr[i]=StrDup(l1.pstr[i]);
  tmp_list.pstr[l1.NumUsed]=StrDup(str);
  tmp_list.NumUsed=l1.NumUsed+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
unsigned i;

  temp_list tmp_list(l2.NumUsed+1);
  if(tmp_list.pstr==NULL) return tmp_list;
  tmp_list.pstr[0]=StrDup(str);
  for(i=0;i<l2.NumUsed;i++)
	tmp_list.pstr[i+1]=StrDup(l2.pstr[i]);
  tmp_list.NumUsed=l2.NumUsed+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)
{
unsigned i, pos;
temp_list tmp_list(l1);

  for(i=0;i<l2.NumUsed;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(pstr==NULL)		// The list is empty.
   {
   *this = str;
   return NumUsed-1;	// returns 0, on alloc failure returns -1
   }

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

#ifndef OptSize

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

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

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


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

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


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.NumUsed<l2.NumUsed?l1.NumUsed:l2.NumUsed);
 while(minnumber-->0)
	{
	if(j=StrCmp(*cpstr1++,*cpstr2++)) return(j);
	}
 if(l1.NumUsed<l2.NumUsed) return -1;
 if(l1.NumUsed>l2.NumUsed) return 1;
return(0);
}


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

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


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

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


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

  for(i=0; i<l1.NumUsed; 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.NumUsed>=l.NumAlloc || l.pstr==NULL)
	{
        void *cpstr = realloc(l.pstr,++l.NumAlloc*sizeof(char *));
	if(cpstr==NULL)
		{
		l.NumUsed=l.NumAlloc=0;
		RaiseError(ListsId|No_Memory,&l);	//Memory Exhausted
		free(str);				//toss str
		return(-1);
		}
        l.pstr = (char **)cpstr;
	}
 l.pstr[l.NumUsed]=str;

return(l.NumUsed++);
}


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

 l.pstr[Pos] = str;

return(l.NumUsed);
}



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

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

  pstr=NULL;
  NumAlloc=NumUsed=0;
}


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

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

if(NumAlloc==0)
	{
	NumUsed=0;
	pstr=NULL;
	return(ListsId | Bad_Size);
	}

if(NumUsed>NumAlloc)
	{
	NumUsed=NumAlloc;
	ret=ListsId | Bad_Number;
	}

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

return(ret);
}


int AddSTRSorted(list & l,TSorter sorter, const char *str, int AllowDupl)
{
unsigned i;
int 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.NumUsed)
	{
	q = sorter(str,*cpstr);
	if(q==0 && !AllowDupl) return(i);
	if(q<=0) break;
	i++;
	cpstr++;
	}
else i=l.NumUsed;

if( (cpstr=(char **)calloc(sizeof(char *),l.NumUsed+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.NumUsed==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.NumUsed-i)*sizeof(char *));
     free(l.pstr);
     }

l.pstr=cpstr;
l.NumAlloc=++l.NumUsed;

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)
{
unsigned hi,lo,pivot;

 if(l.pstr==NULL || l.NumUsed<=0)
 {
   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.NumUsed - 1;
 while(hi>=lo)
 {
   pivot = (lo+hi)/2;
   const int s = l.sorter(str,l.pstr[pivot]);
   if(s==0) return(pivot+1);
   if(s>0)
   {
     lo = pivot+1;
   }
   else		// s<0
   {
     if(pivot==0) break;	// prevent underflow below 0.
     hi = pivot-1;
   }
 }

 return(0);
}


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

if(l.pstr==NULL || l.NumUsed<=1) return(0);
r=0;
apstr=bpstr=l.pstr;
for(i=0;i<l.NumUsed-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.NumUsed-=r;
return(r);
}


#ifdef Streams

#ifndef IOSTREAM_H_ONLY
std::ostream &operator<<(std::ostream &xout, const list &l)
#else
ostream &operator<<(ostream &xout, const list &l)
#endif
{
unsigned i;

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

#endif
