/*******************************************************************************
 *	BString.cc		Beams String Class
 *				T.Barnaby,	BEAm Ltd,	15/7/92
 *		updated by	D.Korchagin,	CERN AB-BI-SW,	2007-08-31
 *******************************************************************************
 */
#ifndef __Lynx__
#else
#include	<stdarg.h>
#endif
#include	<stdio.h>
#include	<string.h>
#include	<stdlib.h>
#include	<stdarg.h>
#include	<ctype.h>
#include	"BString.h"

#ifndef __Lynx__
#else
inline int vasprintf(char **resultp, const char *format, va_list args)
{
	int ret_val;
	int size = 512 * sizeof (char);
	*resultp = (char *) malloc (size);
	while ((((ret_val = vsprintf(*resultp, format, args)) >= size) || (ret_val < 0)) && (*resultp != NULL)) *resultp = (char *) realloc (*resultp, size = (ret_val < 0) ? (2 * size) : (size + sizeof('\0')));
	return ret_val;
}
#endif

#define	DEBUG	0

# define	STRIP	0x7f
# define	MINUS	'-'

static int gmatch(const char *s, const char *p){
	int	scc;
	char	c;

	if (scc = *s++) {
		if ((scc &= STRIP) == 0)
			scc=0200;
	}
	switch (c = *p++) {
	case '[':
		{
			int	ok;
			int	lc;
			int	notflag = 0;

			ok = 0;
			lc = 077777;
			if (*p == '!') {
				notflag = 1;
				p++;
			}
			while (c = *p++) {
				if (c == ']')
					return(ok ? gmatch(s, p) : 0);
				else if (c == MINUS) {
					if (notflag) {
						if (scc < lc || scc > *(p++))
							ok++;
						else
							return(0);
					}
					else {
						if (lc <= scc && scc <= (*p++))
							ok++;
					}
				}
				else {
					lc = c & STRIP;
					if (notflag) {
						if (scc && scc != lc)
							ok++;
						else
							return(0);
					}
					else {
						if (scc == lc)
							ok++;
					}
				}
			}
			return(0);
		}

	default:
		if ((c & STRIP) != scc)
			return(0);

	case '?':
		return(scc ? gmatch(s, p) : 0);

	case '\\':
		c = *p++;
		if((c & STRIP) != scc)
			return(0);
			
	case '*':
		while (*p == '*')
			p++;

		if (*p == 0)
			return(1);
		--s;
		while (*s) {
			if (gmatch(s++, p))
				return(1);
		}
		return(0);

	case '!':
		return !gmatch(--s, p);
	case 0:
		return(scc == 0);
	}
}

BString::~BString(){
	if(ostr && (ostr->deleteRef() == 0)){
		delete ostr;
	}
}

BString::BString() {
	ostr = 0;
}

BString::BString(const BString& string) {
	if(string.ostr)
		ostr = string.ostr->addRef();
	else
		ostr = 0;
}

BString::BString(const char* str) {
	if(str && str[0]){
		ostr = new BRefData(strlen(str) + 1);
		strcpy(ostr->data(), str);
	}
	else {
		ostr = 0;
	}
}

BString::BString(char ch) {
	ostr = new BRefData(2);
	ostr->data()[0] = ch;
	ostr->data()[1] = 0;
}

BString::BString(int value) {
	char	buf[32];

	sprintf(buf, "%d", value);
	ostr = new BRefData(strlen(buf) + 1);
	strcpy(ostr->data(), buf);
}

BString::BString(unsigned int value) {
	char	buf[32];

	sprintf(buf, "%u", value);
	ostr = new BRefData(strlen(buf) + 1);
	strcpy(ostr->data(), buf);
}

BString::BString(long value) {
	char	buf[32];

	sprintf(buf, "%ld", value);
	ostr = new BRefData(strlen(buf) + 1);
	strcpy(ostr->data(), buf);
}

BString::BString(unsigned long long value) {
	char	buf[32];

	sprintf(buf, "%lld", value);
	ostr = new BRefData(strlen(buf) + 1);
	strcpy(ostr->data(), buf);
}

BString::BString(double value) {
	char	buf[32];

	sprintf(buf, "%f", value);
	ostr = new BRefData(strlen(buf) + 1);
	strcpy(ostr->data(), buf);
}

void BString::Init(const char* str) {
	if(str && str[0]){
		ostr = new BRefData(strlen(str) + 1);
		strcpy(ostr->data(), str);
	}
	else {
		ostr = 0;
	}
}

BString BString::convert(char value){
	char	buf[128];

	sprintf(buf, "%c", value);
	return buf;
}

BString BString::convert(int value){
	char	buf[128];

	sprintf(buf, "%d", value);
	return buf;
}

BString BString::convert(unsigned int value){
	char	buf[128];

	sprintf(buf, "%u", value);
	return buf;
}

BString BString::convert(long value){
	char	buf[128];

	sprintf(buf, "%ld", value);
	return buf;
}

BString BString::convert(double value){
	char	buf[128];

	sprintf(buf, "%f", value);
	return buf;
}

BString BString::convert(unsigned long long value){
	char	buf[128];

	sprintf(buf, "%lld", value);
	return buf;
}

BString BString::convertHex(int value){
	char	buf[128];

	sprintf(buf, "0x%x", value);
	return buf;
}

BString BString::convertHex(unsigned int value){
	char	buf[128];

	sprintf(buf, "0x%x", value);
	return buf;
}

BString BString::copy(){
	return BString(retStr());
}

void BString::strChanged(){
}

int BString::len() const {
	if(ostr && ostr->len())
		return ostr->len() - 1;
	else
		return 0;
}

BString& BString::truncate(int l) {
	if(ostr){
		ostr = ostr->copy();
		if(l < len()){
			ostr->setLen(l + 1);
			ostr->data()[l] = '\0';
		}
		strChanged();
	}
	return *this;
}

BString& BString::pad(int l) {
	if(ostr){
		ostr = ostr->copy();
		while(l > len()){
			*this = *this + " ";
		}
		strChanged();
	}
	return *this;
}

BString& BString::toUpper(){
	char*	s;

	if(ostr){
		ostr = ostr->copy();
		for(s = ostr->data(); *s; s++)
			*s = toupper(*s);
	}
	return *this;
}

BString& BString::toLower(){
	char*	s;

	if(ostr){
		ostr = ostr->copy();
		for(s = ostr->data(); *s; s++)
			*s = tolower(*s);
	}
	return *this;
}

void BString::removeNL(){
	if(ostr){
		ostr = ostr->copy();
		if(len() && (ostr->data()[len() - 1] == '\n'))
			truncate(len() - 1);
	}
}

BString BString::subString(int start, int l) const {
	BString	s;

	if(ostr && inString(start) && len()){
		s = &ostr->data()[start];
		if(l >= 0)
			s.truncate(l);
	}
	return s;
}

int BString::inString(int pos) const {
	if((pos >= 0) && (pos <= len()))
		return 1;
	else
		return 0;
}

int BString::del(int start, int l){
	char*	d;
	char*	s;

	if(ostr){
		ostr = ostr->copy();
		if(inString(start) && len()){
			if(l > (len() - start))
				l = len() - start;
			d = &ostr->data()[start];
			s = &ostr->data()[start + l];
			while(*d++ = *s++);
			ostr->setLen(ostr->len() - l);
			strChanged();
			return 1;
		}
	}
	return 0;
}

int BString::insert(int start, BString str){
	BString	s;

	if(inString(start)){
		if(start)
			s = subString(0, start);
		s = s + str;
		s = s + subString(start, -1);
		*this = s;
		strChanged();
		return 1;
	}
	return 0;
}

void BString::printf(const char* fmt, ...)
{
	va_list	ap;
	int	l;
	char*	s = 0;

	if(ostr && ostr->deleteRef() == 0)
		delete ostr;
	ostr = 0;

	va_start(ap, fmt);
	l = vasprintf(&s, fmt, ap);
	va_end(ap);
	if(l >= 0){
		*this = s;
		free(s);
	}
}

int BString::find(char ch) const
{
	char*	a;

	if(ostr && (a = strchr(ostr->data(), ch)))
		return a - ostr->data();
	else
		return -1;
}

int BString::findReverse(char ch) const
{
	char*	a;

	if(ostr && (a = strrchr(ostr->data(), ch)))
		return a - ostr->data();
	else
		return -1;
}

int BString::isSpace(char ch) const {
	return ((ch == ',') || (ch == ' ') || (ch == '\t') || (ch == '\n')
		|| (ch == '\r'));
}

BString BString::field(int field) const {
	char*	ps;
	char*	pe;
	BString	s;

	if(ostr){
		ps = ostr->data();
		while(field && ps && *ps){
			while(*ps && !isSpace(*ps)){
				if(*ps == '"'){
					ps++;
					while(*ps && (*ps != '"'))
						ps++;
				}
				else if(*ps == '('){
					ps++;
					while(*ps && (*ps != ')'))
						ps++;
				}
				ps++;
			}
			while(*ps && isSpace(*ps))
				ps++;
			field--;
		}

		pe = ps;
		while(*pe && !isSpace(*pe)){
			if(*pe == '"'){
				pe++;
				while(*pe && (*pe != '"'))
					pe++;
			}
			else if(*pe == '('){
				pe++;
				while(*pe && (*pe != ')'))
					pe++;
			}
			pe++;
		}
		if(*ps == '"'){
			ps++;
			pe--;
		}
		s = ps;
		s.truncate(pe - ps);
	}
	return s;
}

char** BString::fields(){
	BString	a;
	char**	r;
	char**	o;
	int	f;

	o = r = new char* [64 + 1];

	for(f = 0; f < 64; f++){
		a = field(f);
		if(a != ""){
			*o = new char [a.len() + 1];
			strcpy(*o, a.retStr());
		}
		else {
			break;
		}
		o++;
	}
	*o = 0;
	return r;
}

BList<BString> BString::getTokenList(BString separators){
	BList<BString>	list;
	BString		s = *this;
	BString		t;
	
	while((t = s.pullToken(separators)) != ""){
		list.append(t.removeSeparators(separators));
	}
	return list;
}

BString BString::pullToken(BString terminators){
	char*		p = 0;
	int		s;
	int		e;
	int		n;
	BString		ret;

	if(ostr){
		p = ostr->data();
		s = 0;
		while(*p && strchr(terminators, *p)){
			s++;
			p++;
		}
		e = s;
		while(*p && !strchr(terminators, *p)){
			e++;
			p++;
		}
		n = e;
		while(*p && strchr(terminators, *p)){
			n++;
			p++;
		}

		ret = subString(s, e - s);
		*this = subString(n, -1);
	}
	return ret;
}

BString BString::removeSeparators(BString separators){
	char*		p = 0;
	int		s;
	int		e;

	if(ostr){
		p = ostr->data();
		s = 0;
		while(*p && strchr(separators, *p)){
			s++;
			p++;
		}
		e = len() - 1;
		if(e > 0){
			p = ostr->data() + len() - 1;
			while(*p && strchr(separators, *p)){
				e--;
				p--;
			}
		}
		return subString(s, e - s + 1);
	}
	else {
		return "";
	}
}

BString BString::pullSeparators(BString separators){
	char*		p = 0;
	int		e;
	BString		ret;

	if(ostr){
		p = ostr->data();
		e = 0;
		while(*p && strchr(separators, *p)){
			e++;
			p++;
		}

		ret = subString(0, e);
		*this = subString(e, -1);
	}
	return ret;
}

BString BString::pullWord(){
	return pullToken(" \t\n");
}


BString	BString::pullLine(){
	return pullToken("\n");
}

int BString::compare(const BString& string) const {
	return strcmp(retStr(), string.retStr());
}

int BString::compareWild(const BString& string) const {
	return gmatch(retStr(), string.retStr());
}

int BString::compareWildExpression(const BString& string) const {
	BString	e;
	int	n;
	
	for(n = 0; (e = string.field(n)) != ""; n++){
		if(e[0] == '!'){
			if(gmatch(retStr(), e.retStr() + 1)){
				return 0;
			}
		}
		else {
			if(gmatch(retStr(), e.retStr())){
				return 1;
			}
		}
	}
	return 0;
}

const char* BString::retStr() const {
	if(ostr)
		return ostr->data();
	else
		return "";
}

char* BString::retStrDup() const {
	if(ostr)
		return strdup(ostr->data());
	else
		return strdup("");
}

int BString::retInt() const {
	if(ostr)
		return strtol(ostr->data(), NULL, 0);
	else
		return 0;
}

double BString::retDouble() const {
	if(ostr)
		return strtod(ostr->data(), NULL);
	else
		return 0;
}

BString BString::add(const BString& string) const {
	BString	s;

	if(ostr || string.ostr){
		s.ostr = new BRefData(len() + string.len() + 1);
		s.ostr->data()[0] = '\0';
		if(ostr)
			strcat(s.ostr->data(), ostr->data());
		if(string.ostr)
			strcat(s.ostr->data(), string.ostr->data());
	}
	return s;
}

BString& BString::operator=(const BString& string) {
	if(this != &string){
		if(ostr && (ostr->deleteRef() == 0))
			delete ostr;
		if(string.ostr)
			ostr = string.ostr->addRef();
		else
			ostr = 0;
		strChanged();
	}
	return *this;
}
char& BString::operator[](int pos){
	if(ostr){
		return ostr->data()[pos];
	}
	else {
		fprintf(stderr, "BString: array operator out of range\n");
		exit(1);
	}
}

#ifdef ZAP
BString operator+(const char* a1, BString& a2) {
	return (BString)a1 + a2;
}
#endif

std::ostream& operator<<(std::ostream& o, BString& s){
	o << s.retStr();
	return o;
}

std::istream& operator>>(std::istream& i, BString& s){
	char	buf[1024];

	i >> buf;
	s = (BString)buf;
	return i;
}
