/****************************************************************************
 * This file was derived from the CNCL library by the Pablo research group.
 *
 * The complete CNCL library is available at:
 *       http://www.comnets.rwth-aachen.de/doc/cncl.html
 *
 * The CNCL copyright notice is included here:
 *
 * |_|_|_  |_|_    |_    |_|_|_  |_               C O M M U N I C A T I O N
 *|_       |_  |_  |_  |_        |_                         N E T W O R K S
 *|_       |_  |_  |_  |_        |_                               C L A S S
 * |_|_|_  |_    |_|_    |_|_|_  |_|_|_|_                     L I B R A R Y
 *
 *
 *
 * Copyright (C) 1992-1998   Communication Networks
 *                           Aachen University of Technology
 *                           D-52056 Aachen
 *                           Germany
 *                           Email: cncl-adm@comnets.rwth-aachen.de
 *
 *
 * This library is free  software; you can  redistribute it and/or modify
 * it  under   the terms  of the GNU   Library  General Public License as
 * published by the  Free Software Foundation;  either  version 2 of  the
 * License, or  (at  your option)  any  later  version.  This  library is
 * distributed  in the  hope  that  it  will  be useful,  but WITHOUT ANY
 * WARRANTY;  without even the   implied  warranty of MERCHANTABILITY  or
 * FITNESS FOR A PARTICULAR PURPOSE.   See the GNU Library General Public
 * Library General Public License along with  this library; if not, write
 * to the Free Software Foundation,  675  Mass Ave, Cambridge, MA  02139,
 * USA.
 ****************************************************************************/

//   -*- C++ -*-
/*****************************************************************************
 *
 * FlClass: FlString --- Character string
 *
 *****************************************************************************/

#include <string.h>
#include <ctype.h>

#include "FlString.h"

using std::istream;
using std::endl;

/*
 * Comparison operators
 */
Fl_bool operator <  (const FlString& a, const FlString& b)
{   return strcmp(a.p, b.p) <  0; }	

Fl_bool operator >  (const FlString& a, const FlString& b)
{   return strcmp(a.p, b.p) >  0; }	

Fl_bool operator >= (const FlString& a, const FlString& b)
{   return strcmp(a.p, b.p) >= 0; }

Fl_bool operator <= (const FlString& a, const FlString& b)
{   return strcmp(a.p, b.p) <= 0; }

Fl_bool operator == (const FlString& a, const FlString& b)
{   return strcmp(a.p, b.p) == 0; }

Fl_bool operator != (const FlString& a, const FlString& b)
{   return strcmp(a.p, b.p) != 0; }



/***** FlString private functions *******************************************/
 
void FlString::init(unsigned l, unsigned extra)
{
    len = l;
    alloc = len + extra;
    p = new char[alloc + 1];
    p[len] = '\0';
}



/*
 * Copy from string of length l (l+1 == length + '\0')
 *
 * DOES NOT SET len !!!
 */
void FlString::cpyn(const char *s, unsigned l)
{
    unsigned n = l+1>alloc ? alloc : l+1;

    strncpy(p, s, n);
    p[n] = 0;
}

    
    
/***** FlString constructors ************************************************/

FlString::FlString(int extra)
{
    if ( extra < 0 ) extra = default_extra;
    init(0,extra);
}

FlString::FlString(char c)
{
    init(2);
    p[0] = c;
    p[1] = 0;
}

FlString::FlString(char c, int extra)
{
    if (extra < 0)
	extra = default_extra;
    init(2, extra);
    p[0] = c;
    p[1] = 0;
}

FlString::FlString(const char* cs)
{ 
    unsigned l = strlen(cs);
    init(l);
    cpyn(cs, l);
}


FlString::FlString(const char* cs, int extra)
{
    if ( extra < 0 ) extra = default_extra;
    unsigned l = strlen(cs);
    init(l, extra);
    cpyn(cs, l);
}

FlString::FlString(const FlString& s)
{
    init(s.len);
    cpyn(s.p, s.len);
}

FlString::FlString(const FlString& s, int extra)
{
    if (extra < 0) extra = default_extra;
    init(s.len, extra);
    cpyn(s.p, s.len);
}

FlString::~FlString() 
{
    delete [] p;
}



/***** public functions ******************************************************/
 
void FlString::resize(unsigned new_capacity) 
/* sets capacity alloc of FlString to new_capacity */
{
    if ( new_capacity != alloc )
    {
	alloc = new_capacity;
	char* tmp = p;
	p = new char[alloc + 1];
	if(len > alloc)
	    len = alloc;
	cpyn(tmp, len);
	delete [] tmp;
    }
}    	


void FlString::to_lower()
/* sets all letters in FlString to lower case */
{
    for(unsigned long i=0; i<len; i++)
	if(isupper(p[i]))
	    p[i] = tolower(p[i]);
}



void FlString::to_upper()
/* sets all letters to upper case */
{
    for(unsigned long i=0; i<len; i++)
	if(islower(p[i]))
	    p[i] = toupper(p[i]);
}



void FlString::capitalize()
/* sets all first letters of the words (after a space) to upper case, all 
 * other letters will be lower case */
{
    unsigned long flag, i;
    
    for(i=0, flag=TRUE; i<len; i++)
    {
	if(isspace(p[i]))
	    flag = TRUE;
	else if(flag)
	{
	    flag = FALSE;
	    if(islower(p[i]))
		p[i] = toupper(p[i]);
	}
	else 
	{
	    if(isupper(p[i]))
		p[i] = tolower(p[i]);
	}
    }
}



/*
 * Remove '\r' and/or '\n' at end of string
 */
void FlString::strip_crlf()
{
    if(len > 0  &&  p[len-1] == '\n')
    {
	p[len-1] = 0;
	len--;
    }
    
    if(len > 0  &&  p[len-1] == '\r')
    {
	p[len-1] = 0;
	len--;
    }
}



/*
 * Remove white space
 */
void FlString::strip_lspace()
{
    unsigned long l=0;

    while(l<len && isspace(p[l]))
	l++;
    if(l)
	del(0, l);
}

void FlString::strip_rspace()
{
    int l=0;

    while(((unsigned long)l) < len && isspace(p[len-1-l]))
	l++;
    if(l)
	del(-l, l);
}

void FlString::strip_space()
{
    strip_lspace();
    strip_rspace();
}



FlString FlString::after( int pos, int l)  const
/* returns the l characters after position pos (zero based) as a FlString; 
 * char at pos is first of FlString; default value l = 0 returns all 
 * characters until end of FlString */ 
{
    FlString r;
    int i;
    
    if(l <= 0)
	l = len;
    
    if(pos>= 0 && ((unsigned long)pos)<len)
    {
	int m = (((unsigned long)(pos + l)) > len) ? len : pos+l;
	int n = m - pos;
	
	r.resize(n + default_extra);
	
	for(i=0; i<n; i++)
	    r.p[i] = p[pos+i];
	r.p[i] = 0;
	r.len  = n;
    }

    return r;
}



FlString FlString::before( int pos, int l) const
/* returns the l characters before position pos (zero based) as a FlString;
 * char at pos is last of FlString; default value l = 0 returns all characters 
 * until position zero */
{
    FlString r;
    int i;
    
    if(l <= 0)
	l = len;
    
    if(pos>= 0 && ((unsigned long)pos)<=len)
    {
	int m = pos;
	pos -= l;
	if(pos < 0)
	    pos = 0;
	int n = m - pos;
	
	r.resize(n + default_extra);
	
	for(i=0; i<n; i++)
	    r.p[i] = p[pos+i];
	r.p[i] = 0;
	r.len  = n;
    }

    return r;
}



FlString& FlString::add(char c)
/* adds character c at the end of FlString this */
{
    if(len >= alloc)
    	resize(len + default_extra);
    p[len++] = c;
    p[len]   = '\0';
    return *this;
}



FlString& FlString::add(const char *cs)
/* adds characterstring cs at the end of FlString this */
{
    insert(cs, len);
    return *this;
}



/***** Delete chars *********************************************************/

FlString& FlString::del(int pos, int l)
/* deletes l characters beginning at position pos (zero based); default values
 * for pos is zero, for l is 1 ( e.g. del() will delete first char of this).
 * If l is set to 0 all characters beginning at pos will be deleted ( e.g. 
 * del(0,0) will return an empty FlString) */
{
    if(l <= 0)
	l = len;

    /* pos<0 counts from end of string */
    if(pos < 0)
    {
	pos = len + pos;
	if(pos < 0)
	    pos = 0;
    }
    
    if(pos>=0 && ((unsigned long)pos)<len)
    {
	int m = (((unsigned long)(pos + l)) > len) ? len : pos+l;
	int n = m - pos;

	while(((unsigned long)m) <= len)
	    p[pos++] = p[m++];
	
	len -= n;
	p[len] = 0;
    }

    return *this;
}



FlString& FlString::del(char c, int pos) 
/* deletes the first char c in this after pos (zero based) or nothing if c 
 * is not in this after pos. Default value for pos is zero. */
{
    pos = upsearch(c, pos);
    if(pos != FlERROR)
	del(pos, 1);			/* Delete 1 char */

    return *this;
}


    
FlString& FlString::del(const FlString& s, int pos) 
/* deletes the first occurence of FlString s in FlString this after position pos 
 * (zero based) or nothing if s not in this after pos; default value for pos 
 * is zero */    
{
    pos = upsearch(s, pos);
    if(pos != FlERROR)
	del(pos, s.len);

    return *this;
}



FlString& FlString::del(const char *s, int pos)
/* deletes the first occurence of cs in in FlString this after position pos
 * (zero based) or nothing if cs is not this after pos; default value for 
 * pos is zero */
{
    pos = upsearch(s, pos);
    if(pos != FlERROR)
	del(pos, strlen(s));

    return *this;
}



/***** Insert chars *********************************************************/

FlString& FlString::insert ( char c, int pos)
/* inserts character c at position pos (zero based); default value for pos 
 * is zero */
{
    if ( pos >= 0  &&  ((unsigned long)pos) <= len ) 
    {
	int i;
	
	if ( len >= alloc ) resize( len + default_extra );
	for(i=len; i>=pos; i--)
	    p[i+1] = p[i];
	len++;
	p[len] = 0;
	p[pos] = c;
    }
    return *this;
}



FlString& FlString::insert(const char* cs, int pos)
/* inserts char* cs at position pos (zero based); default value for pos is
 * zero */ 
{
    if ( pos >= 0  &&  ((unsigned long)pos) <= len )
    {
	int l = strlen(cs);
	int i;
	
	resize ( l + len + default_extra );
	
	for(i=len; i>=pos; i--)
	    p[i+l] = p[i];
	len += l;
	p[len] = 0;
	for(i=0; i<l; i++)
	    p[pos+i] = cs[i];
    }
    return *this;
}

	 
FlString& FlString::insert (const FlString& s, int pos)
/* inserts FlString s at position pos (zero based); default value for pos is 
 * zero */

{
    return insert(s.p, pos);
}



/***** Replace chars ********************************************************/

FlString& FlString::replace(char c, int pos)
/* replaces the character at position pos (zero based) with c, default value
 * for pos is zero */
{
    if(pos>=0 && ((unsigned long)pos) <len)
	p[pos] = c;
    return *this;
}


FlString& FlString::replace(char oldc, char newc, int pos)
/* replaces the first character oldc after position pos (zero based) with newc,
 * default value for pos is zero */
{
    pos = upsearch(oldc, pos);
    if(pos != FlERROR)
	p[pos] = newc;

    return *this;
}


FlString& FlString::replace(const FlString& s, int pos, int l)
/* replaces the l characters, starting at position pos (zero based, default 
 * value zero), with FlString s; default value l = 0 will delete as many 
 * characters as the length of FlString s */    
{
    if(pos>=0 && ((unsigned long)pos) <=len)
    {
	del(pos, l);
	insert(s, pos);
    }
    
    return *this;
}


FlString& FlString::replace(const FlString& olds,const FlString& news, int pos)
/* replaces the first occurence of FlString olds after position pos with 
 * FlString news, default value for pos is zero. If olds is not in this
 * nothing is changed */
{	    
    pos = upsearch(olds, pos);
    if(pos != FlERROR)
    {
	del(pos, olds.len);
	insert(news, pos);
    }

    return *this;
}



/***** Search ***************************************************************/

/*
 * Search for a char or a string, starting at `pos' and working towards
 * the _start_ of the string. Return the index if found, else FlERROR.
 */
int FlString::downsearch (char c, int pos) const  
{
    int i;

    if (pos <= 0 || ((unsigned long)pos) >= len)
	pos = len - 1;

    for(i=pos; i>=0; i--)
	if(p[i] == c)
	    return i;
    
    return FlERROR;
}


int FlString::downsearch (const FlString& s, int pos) const 
{
    int i;

    if (pos <= 0 || ((unsigned long)pos) >= len)
	pos = len - 1;

    for(i=pos; i>=0; i--)
	if(matches(s, i))
	    return i;
    
    return FlERROR;
}


int FlString::downsearch (const char *s, int pos) const 
{
    int i;

    if (pos <= 0 || ((unsigned long)pos) >= len)
	pos = len - 1;     

    for(i=pos; i>=0; i--)
	if(matches(s, i))
	    return i;
    
    return FlERROR;
}



/*
 * Search for a char or a string, starting at `pos' and working towards
 * the _end_ of the string. Return the index if found, else FlERROR.
 */
int FlString::upsearch (char c, int pos) const 
{
    int i;

    if(pos < 0)
	pos = 0;
    
    for(i=pos; ((unsigned long)i) < len; i++)
	if(p[i] == c)
	    return i;
    
    return FlERROR;
}


int FlString::upsearch (const FlString& s, int pos) const
{
    int i;
    
    if(pos < 0)
	pos = 0;
    
    for(i=pos; ((unsigned long)i) < len; i++)
	if(matches(s, i))
	    return i;
    
    return FlERROR;
}


int FlString::upsearch(const char *s, int pos) const
{
    int i;
    
    if(pos < 0)
	pos = 0;
    
    for(i=pos; ((unsigned long)i)<len; i++)
	if(matches(s, i))
	    return i;
    
    return FlERROR;
}



/*
 * Returns TRUE, if the argument string is to be found at `pos', else FALSE
 */
Fl_bool FlString::matches(const char *cs, int pos) const
{
    int l = strlen(cs);

    if(pos>=0 && ((unsigned long)pos) <= len - l)
    {
	if(cs[0] == p[pos])
	    return strncmp(cs, p+pos, l) == 0;
	else
	    return FALSE;
    }
    
    return FALSE;
}


Fl_bool FlString::matches(const FlString& s, int pos) const
{   
    return matches(s.p, pos);
}



/*
 * Input string from IOStream
 */
void FlString::icopy( istream& strm )
{
    char tmp[512];
    strm.getline(tmp, sizeof(tmp));
    len = strlen(tmp);
    resize(len + default_extra);
    cpyn(tmp, len);
}



/***** operators *************************************************************/

char FlString::operator()(int i) const
{
    if(i < 0)
	i = len + i;
    if(i<0 || ((unsigned long)i) >= len)
	return '\0';
    return p[i];
}



char& FlString::operator[](int i)
{
    static char cc = '\0';

    if(i < 0)
	i = len + i;
    if(i<0 || ((unsigned long)i) >= len)
	return cc;
    return p[i];
}



void FlString::operator=(const FlString& s)
{
    if (p != s.p)
    {
	delete [] p;
	init(s.len);
	cpyn(s.p, s.len);
    }
}


void FlString::operator=(const char *cs)
{
    if (p != cs)
    {
	delete [] p;
	unsigned l = strlen(cs);
	init(l);
	cpyn(cs, l);
    }
}


void FlString::operator=(char c)
{
    delete [] p;
    init(1);
    p[0] = c;
    p[1] = '\0';
}



FlString operator + (const FlString& s1, const FlString& s2)
{
    FlString t(s1);
    t.add(s2);
    return t;
}



FlString& FlString::operator += (const FlString& s) 
{
    add(s);
    return *this;
}

FlString& FlString::operator += (const char* cs)
{     
    add(cs);
    return *this;
}

	
 

/***** Default I/O member function for FuzzyLib classes **************************/

// FlNormal output
void FlString::print(ostream &strm) const
{
    strm << p;
}

// Debug output
void FlString::dump(ostream &strm) const
{
    strm << "FlString { $Revision: 1.3 $  len=" << len << " alloc="
	 << alloc << " p=\"" << p << "\" }" << endl;
}

// input
istream &operator >> (istream& strm, FlString& s)
{
    s.icopy(strm);
    return strm;
}



/***** FuzzyLib stuff for type information and exemplar objects ******************/

