/* $Id: str.c,v 1.7 2003/07/29 22:35:18 mastermind Exp $ */

/*
 * Copyright (c) 1998-2003, Alexander Marx
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *    - Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer. 
 *    - Redistributions in binary form must reproduce the above
 *      copyright notice, this list of conditions and the following
 *      disclaimer in the documentation and/or other materials provided
 *      with the distribution. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 *
 */

#define __easy_eSTR_C

#include "str.h"


/* --- Public Functions --------------------------------------------------- */

eSTR *va_newSTR(const char *s, ...)
{
	va_list strs;
	eSTR *str=NULL;
	const char *t;
	long len=0;

	va_start(strs, s);	
	while(t=va_arg(strs, const char *)) {
		len+=strlen(t);
	}
	va_end(strs);

	if(str=malloc(sizeof(*str))) {
		init(str);
		if(str->str=malloc(strlen(s)+len+1)) {
			strcpy(str->str, s);

			va_start(strs, s);
			while(t=va_arg(strs, const char *))
				strcat(str->str, t);
			va_end(strs);

			str->len=strlen(s)+len;
		} else {
			free(str);
			str=NULL;
		}
	}
	return str;
}

eSTR *newSTR(const char *s)
{
	eSTR *str=NULL;

	if(str=malloc(sizeof(*str))) {
		init(str);
		if(s) {
			if(str->str=malloc(strlen(s)+1)) {
				strcpy(str->str, s);
				str->len=strlen(s);
			} else {
				free(str);
				str=NULL;
			}
		}
	}
	return str;
}


eSTR *clrSTR(eSTR *str)
{
	if(str)
		destroy(str);
	return str;
}


eSTR *cpySTR(eSTR *dest_str, const eSTR *src_str)
{
	eSTR tmp_str;
	
	if(dest_str && src_str && (dest_str!=src_str)) {
		if(copy(&tmp_str, src_str)) {
			destroy(dest_str);
			*dest_str=tmp_str;
			return dest_str;
		}
	}
	return NULL;
}


eSTR *dupSTR(const eSTR *src_str)
{
	eSTR *str=NULL;
	
	if(src_str) {
		if(str=malloc(sizeof(*str))) {
			if(!copy(str, src_str)) {
				free(str);
				str=NULL;
			}
		}
	}
	return str;
}


eSTR *setSTR(eSTR *str, const char *s)
{	
	char *t;
	
	if(str) {
		if(s) {
			if(t=malloc(strlen(s)+1)) {
				strcpy(t, s);
				destroy(str);
				str->str=t;
				str->len=strlen(s);
			} else
				return NULL;
		} else
			destroy(str);
	}
	return str;
}


eSTR *spcSTR(unsigned long len)
{
	eSTR *str=NULL;

	if(len>=0) {
		if(str=malloc(sizeof(*str))) {
			init(str);
			if(str->str=malloc(len+1)) {
				memset(str->str, ' ', len);
				str->str[len]='\0';
				str->len=len;
			} else {
				free(str);
				str=NULL;
			}
		}
	}
	return str;
}


void freeSTR(eSTR *str)
{
	if(str) {
		destroy(str);
		free(str);
	}
}


char *strSTR(const eSTR *str)
{
	return str ? str->str : NULL;
}


long lenSTR(const eSTR *str)
{
	return str ? str->len : 0;
}


int putSTR(const eSTR *str, FILE *fh)
{
	int rc=0;

	if(fh && str && str->str)
		rc=fprintf(fh, "%s", str->str);
	return rc;
}


int putsSTR(const eSTR *str, FILE *fh)
{
	int rc=0;

	if(fh) {
		if(str && str->str)
			rc=fprintf(fh, "%s", str->str);
		rc+=fprintf(fh, "\n");
	}
	return rc;
}


eSTR *getSTR(FILE *fh)
{
	int c;
	char *buffer=NULL;
	eSTR *str=NULL;
	long len=-1;
	
	if(fh) {
		if(feof(fh))
			return NULL;
		if(str=newSTR(NULL)) {
			if(buffer=malloc(eSTR_BLOCK_SIZE)) {
				buffer[eSTR_BLOCK_SIZE-1]='\0';
				for(;;) {
					c=fgetc(fh);
					len++;
					buffer[len]=(char)c;
					if(c=='\0' || c==EOF) {
						buffer[len]='\0';
						if(catSTR(str, buffer))
							break;
						else {
							freeSTR(str);
							str=NULL;
							break;
						}
					}
					if(len==(eSTR_BLOCK_SIZE-2)) {
						if(!catSTR(str, buffer)) {
							freeSTR(str);
							str=NULL;
							break;
						} else {
							len=-1;
						}
					}
				}
				free(buffer);
			} else {
				freeSTR(str);
				str=NULL;
			}
		}
	}
	return str;
}


eSTR *getlineSTR(FILE *fh)
{
	int c;
	char *buffer=NULL;
	eSTR *str=NULL;
	long len=-1;
	
	if(fh) {
		if(feof(fh))
			return NULL;
		if(str=newSTR(NULL)) {
			if(buffer=malloc(eSTR_BLOCK_SIZE)) {
				buffer[eSTR_BLOCK_SIZE-1]='\0';
				for(;;) {
					c=fgetc(fh);
					len++;
					buffer[len]=(char)c;
					if(c=='\n' || c=='\r' || c=='\0' || c==EOF) {
						if(c=='\r') {
							if((c=fgetc(fh))!='\n')
								ungetc(c, fh);
						}
						buffer[len]='\0';
						if(catSTR(str, buffer))
							break;
						else {
							freeSTR(str);
							str=NULL;
							break;
						}
					}
					if(len==(eSTR_BLOCK_SIZE-2)) {
						if(!catSTR(str, buffer)) {
							freeSTR(str);
							str=NULL;
							break;
						} else {
							len=-1;
						}
					}
				}
				free(buffer);
			} else {
				freeSTR(str);
				str=NULL;
			}
		}
	}
	return str;
}


char traverseSTR(eSTR *str, int move_to)
{
	if(str) {
		switch(move_to) {
			case eSTR_HEAD:
				str->idx=0;
				break;
			
			case eSTR_TAIL:
				str->idx=str->len;
				break;

			case eSTR_FWD:
				str->idx++;
				break;
			
			case eSTR_BWD:
				str->idx--;
				break;
		}
	}
	return chrSTR(str);
}


char *adrSTR(const eSTR *str)
{
	return str ? str->str ? str->str+str->idx : NULL : NULL;
}


long idxSTR(const eSTR *str)
{
	return str ? str->idx : 0;
}


char chrSTR(eSTR *str)
{
	char c='\0';
	
	if(str) {
		if(str->str) {
			if(str->idx<0) {
				c='\0';
				str->idx=0;
			} else if(str->idx>str->len) {
				c='\0';
				str->idx=str->len;
			} else {
				c=str->str[str->idx];
			}
		}
	}
	return c;
}


eSTR *upperSTR(eSTR *str)
{
	char *t;

	if(str && (t=str->str)) {
		while(*t) {
			if(islower(*t))
				*t=toupper(*t);
			t++;
		}
	}
	return str;
}


eSTR *lowerSTR(eSTR *str)
{
	char *t;

	if(str && (t=str->str)) {
		while(*t) {
			if(isupper(*t))
				*t=tolower(*t);
			t++;
		}
	}
	return str;
}


eSTR *reverseSTR(eSTR *str)
{
	long n, m;
	char c;
	
	if(str && str->str) {
		for(n=0, m=str->len-1; n<(str->len)/2; n++, m--) {
			c=str->str[n];
			str->str[n]=str->str[m];
			str->str[m]=c;
		}
	}
	return str;
}


eSTR *stripSTR(eSTR *str, int where, const char *s)
{
	long m, n, i;
	
	if(str && str->str && s) {
		if(BTST(where, eSTR_LEAD) && str->len) {
			for(i=0; strchr(s, str->str[i]); i++);
			str->len-=i;
			if(str->len<0)
				str->len=0;
			else
				memmove(str->str, str->str+i, (unsigned int)str->len);
		}
		if(BTST(where, eSTR_TRAIL) && str->len) {
			i=str->len-1;
			if(i>=0) {
				while(strchr(s, str->str[i]))
					i--;
				str->str[++i]='\0';
				str->len=i;
			}
		}
		if(BTST(where, eSTR_BODY) && str->len) {
			if(str->len>0) {
				for(i=0; strchr(s, str->str[i]); i++);
				for(n=str->len-1; strchr(s, str->str[n]); n--);
				for(m=i; str->str[i]; i++)
					if((strchr(s, str->str[i])==NULL) || (i>n)) {
						str->str[m]=str->str[i];
						m++;
					}
				str->str[m]='\0';
				str->len=m;
			}
			
		}
	}
	return str;
}


eSTR *catSTR(eSTR *dest_str, const char *s)
{
	long len;
	char *t;
	
	if(dest_str) {
		if(s) {
			if(t=malloc(dest_str->len+strlen(s)+1)) {
				if(dest_str->str)
					strcpy(t, dest_str->str);
				else
					*t='\0';
				strcat(t, s);
				len=dest_str->len+strlen(s);
				destroy(dest_str);
				dest_str->str=t;
				dest_str->len=len;
				return dest_str;
			}
		} else
			return dest_str;
	}
	return NULL;
}

eSTR *va_catSTR(eSTR *dest_str, ...)
{
	va_list strs;
	const char *s;
	char *t;
	size_t len=0;

	if(!dest_str)
		return NULL;

	va_start(strs, dest_str);	
	while(s=va_arg(strs, const char *)) {
		len+=strlen(s);
	}
	va_end(strs);

	va_start(strs, dest_str);	
	if(t=malloc(dest_str->len+len+1)) {
		if(dest_str->str)
			strcpy(t, dest_str->str);
		else
			*t='\0';
		while(s=va_arg(strs, const char *))
			strcat(t, s);

		len=dest_str->len+len;
		destroy(dest_str);
		dest_str->str=t;
		dest_str->len=len;
		return dest_str;
	}
	va_end(strs);

	return NULL;
}


char *tokSTR(eSTR *str, const char *s)
{
	if(str)
		return strtok(str->str, s);
	else
		return strtok(NULL, s);
		
}


eSTR *catSTRs(eSTR *dest_str, const eSTR *src_str)
{
	if(dest_str && src_str && catSTR(dest_str, src_str->str))
		return dest_str;
	else
		return NULL;
}


eSTR *leftSTR(const eSTR *str, long idx)
{
	eSTR *left=NULL;

	if(str) {
		if(idx>=str->len)
			left=dupSTR(str);
		else if(idx<=0)
			left=newSTR(NULL);
		else {
			if(left=spcSTR((unsigned long)idx))
				memcpy(left->str, str->str, (size_t) idx);
		}
	}
	return left;
}


eSTR *rightSTR(const eSTR *str, long idx)
{
	eSTR *right=NULL;

	if(str) {
		if(idx>=str->len)
			right=newSTR(NULL);
		else if(idx<0)
			right=dupSTR(str);
		else {
			if(right=spcSTR((unsigned long)str->len-idx-1))
				memcpy(right->str, str->str+idx+1, (size_t) str->len-idx-1);
		}
	}
	return right;
}


eSTR *midSTR(const eSTR *str, long idx, long len)
{
	eSTR *mid=NULL;
	long start, end;

	if(str) {
		if(len<0)
			mid=rightSTR(str, idx);
		else {
			end=idx+len+1;
			if(end>str->len)
				end=str->len;
			start=idx;
			if(start<0)
				start=-1;
			
			if(idx>=str->len)
				mid=newSTR(NULL);
			else if(mid=spcSTR((unsigned long)end-start-1)) {
				memcpy(mid->str, str->str+start+1, (size_t) end-start-1);
			}
		}
	}
	return mid;
}


long subsSTR(const eSTR *haystack, const char *needle)
{
	char *s;
	long idx=-1;
	
	if(haystack && needle && haystack->str) {
		if(s=strstr(haystack->str, needle)) {
			idx=s-haystack->str;
		}
	}
	return idx;
}


long subcSTR(const eSTR *haystack, char needle)
{
	char *s;
	long idx=-1;
	
	if(haystack && needle && haystack->str) {
		if(s=strchr(haystack->str, needle)) {
			idx=s-haystack->str;
		}
	}
	return idx;
}


int cmpSTR(const eSTR *str, const char *s)
{
	if(str && str->str) {
		if(s)
			return strcmp(str->str, s);
		else
			return 1;
	} else if(s)
		return -1;
	else
		return 0;
}


int cmpSTRs(const eSTR *s1, const eSTR *s2)
{
	if(s1 && s1->str) {
		if(s2 && s2->str)
			return strcmp(s1->str, s2->str);
		else
			return 1;
	} else if(s2 && s2->str)
		return -1;
	else
		return 0;
}

BOOL verifySTR(const eSTR *str, const char *s)
{
	BOOL rc=FALSE;
	char *t;
	
	if(str && str->str && s) {
		t=str->str;
		rc=TRUE;
		while(*t && rc) {
			if(strchr(s, *t)==NULL)
				rc=FALSE;
			t++;
		}
	}
	return rc;
}


eSTR *infileSTR(const char *file) {
	eSTR *str=NULL;
	long size;
	FILE *fh;
	
	if(fh=fopen(file, "r")) {
		if(str=newSTR(NULL)) {
			fseek(fh, 0, SEEK_END);
			size=ftell(fh);
			rewind(fh);
			if(str->str=malloc((unsigned long)size+1)) {
					fread(str->str, (size_t) size, 1, fh);
					str->str[size]='\0';
					str->len=size;
			} else {
				freeSTR(str);
				str=NULL;
			}
		}
		fclose(fh);
	}
	return str;
}


int outfileSTR(const eSTR *str, const char *file) {
	int rc=0;
	FILE *fh;
	
	if(str && file) {
		if(fh=fopen(file, "w")) {
			rc=fwrite(str->str, (size_t)str->len, 1, fh);
			fclose(fh);
		}
	}
	return rc;
}

int tounixSTR(eSTR *str) {
	char *s;
	int i=0;

	if(str) {
		
		for(i=0, s=str->str; *s; *(++s-i)=*s) {
			if(*s=='\r') i++; 
		}
		str->len=str->len-i;
		*(str->str+str->len)='\0';
	}
	return i;
}


/* --- Private Functions -------------------------------------------------- */

static int copy(eSTR *dest_str, const eSTR *src_str)
{
	int rc=FALSE;
	
	if(dest_str->str=malloc((unsigned long) src_str->len+1)) {
		memcpy(dest_str->str, src_str->str, (size_t) src_str->len+1);
		dest_str->len=src_str->len;
		dest_str->idx=src_str->idx;
		rc=TRUE;
	}
	return rc;
}


static void destroy(eSTR *str)
{
	if(str) {
		if(str->str)
			free(str->str);
		init(str);
	}
}


static void init(eSTR *str)
{
	if(str) {
		str->str=NULL;
		str->len=str->idx=0;
	}
}
