/*
 * Miscellaneous string-handling related utility-functions
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2002-2004 Tecnic Software productions (TNSP)
 *
 * Please read file 'COPYING' for information on license and distribution.
 */
#include "th_string.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>


/* Convert given character to lower case
 */
int th_tolower(int c)
{
 if ((c >= 'A') && (c <= 'Z'))
 	c += ('a' - 'A');
 	else
 	switch (c) {
 	case 'Ä': c = 'ä'; break;
 	case 'Ö': c = 'ö'; break;
 	case 'Å': c = 'å'; break;
 	}

 return c;
}


/* Convert given character to upper case
 */
int th_toupper(int c)
{
 if ((c >= 'a') && (c <= 'z'))
 	c += ('A' - 'a');
 	else
 	switch (c) {
 	case 'ä': c = 'Ä'; break;
 	case 'ö': c = 'Ö'; break;
 	case 'å': c = 'Å'; break;
 	}

 return c;
}


/* Copy a given string "over" into *ppResult.
 */
int th_strcpy(char **ppResult, const char *pStr)
{
 assert(ppResult);
 
 /* Check the string pointers */
 if (!pStr) return -1;

 /* Allocate memory for destination */
 if (*ppResult) free(*ppResult);
 *ppResult = (char *) malloc(strlen(pStr) + 1);
 if (!*ppResult) return -2;

 /* Copy to the destination */
 strcpy(*ppResult, pStr);

 return 0;
}


/* Concatenates a given string into string pointed by *ppResult.
 */
int th_strcat(char **ppResult, const char *pStr)
{
 assert(ppResult);

 /* Check the string pointers */
 if (!pStr) return -1;

 if (*ppResult != NULL)
 	{
 	*ppResult = (char *) realloc(*ppResult, strlen(*ppResult) + strlen(pStr) + 1);
	if (*ppResult == NULL) return -1;
	strcat(*ppResult, pStr);
	} else
	{
	*ppResult = (char *) malloc(strlen(pStr) + 1);
	if (*ppResult == NULL) return -1;
	strcpy(*ppResult, pStr);
	}

 return 0;
}


/* Return a copy of given string
 */
char *th_strdup(const char *pStr)
{
 char *pResult;
 
 /* Check the string pointers */
 if (!pStr) return NULL;

 /* Allocate memory for destination */
 pResult = (char *) malloc(strlen(pStr) + 1);
 if (!pResult) return NULL;

 /* Copy to the destination */
 strcpy(pResult, pStr);

 return pResult;
}


/* Compare two strings, case-INSENSITIVELY
 */
int th_strcasecmp(char *pStr1, char *pStr2)
{
 assert(pStr1);
 assert(pStr2);
 
 /* Check the string pointers */
 if (pStr1 == pStr2) return 0;

 /* Go through the string */
 while (*pStr1 && *pStr2 && (th_tolower(*pStr1) == th_tolower(*pStr2))) { pStr1++; pStr2++; }

 return (th_tolower(*pStr1) - th_tolower(*pStr2));
}


/* Remove all occurences of control characters, in-place.
 */
void th_strip_ctrlchars(char *pStr)
{
 int i = 0, j = 0;
 assert(pStr);

 while (pStr[i] != 0)
	{
	if (!th_iscntrl(pStr[i]))
		pStr[j++] = pStr[i];
	i++;
	}

 pStr[j] = 0;
}


/* Find next non-whitespace character in string.
 * Updates iPos into the position of such character and
 * returns pointer to the string.
 */
char *th_findnext(char *pStr, size_t *iPos)
{
 assert(pStr);
 
 /* Terminating NULL-character is not whitespace! */
 while (th_isspace(pStr[*iPos])) (*iPos)++;
 return &pStr[*iPos];
}


/* Find next chSep-character from string
 */
char *th_findsep(char *pStr, size_t *iPos, char chSep)
{
 assert(pStr);
 
 /* Terminating NULL-character is not digit! */
 while (pStr[*iPos] && (pStr[*iPos] != chSep)) (*iPos)++;
 return &pStr[*iPos];
}


/* Find next chSep- or whitespace from string
 */
char *th_findseporspace(char *pStr, size_t *iPos, char chSep)
{
 assert(pStr);
 
 /* Terminating NULL-character is not digit! */
 while (!th_isspace(pStr[*iPos]) && (pStr[*iPos] != chSep)) (*iPos)++;
 return &pStr[*iPos];
}


/* Compare a string to a pattern. Case-SENSITIVE version.
 * The matching pattern can consist of any normal characters plus
 * wildcards ? and *. "?" matches any character and "*" matches
 * any number of characters.
 */
BOOL th_strmatch(char *pStr, char *pPattern)
{
 BOOL didMatch, isAnyMode, isEnd;
 char *tmpPattern;

 /* Check given pattern and string */
 if (!pStr) return FALSE;
 if (!pPattern) return FALSE;

 /* Initialize */
 tmpPattern = NULL;
 didMatch = TRUE;
 isEnd = FALSE;
 isAnyMode = FALSE;
 
 /* Start comparision */
 do 
 {
 didMatch = FALSE;
 switch (*pPattern) {
 case '?':
	/* Any single character matches */
	if (*pStr)
		{
		didMatch = TRUE;
		pPattern++;
		pStr++;
		}
	break;

 case '*':
 	didMatch = TRUE;
 	pPattern++;
 	if (!*pPattern) isEnd = TRUE;
 	isAnyMode = TRUE;
 	tmpPattern = pPattern;
 	break;

 case 0:
	if (isAnyMode)
	{
	if (*pStr)
		pStr++;
		else
		isEnd = TRUE;
	} else {
	if (*pStr)
		{
		if (tmpPattern)
			{
			isAnyMode = TRUE;
			pPattern = tmpPattern;
			} else
			didMatch = FALSE;
		} else
		isEnd = TRUE;
	}
 	break;
 default:
	if (isAnyMode)
	{
	if ((*pPattern) == (*pStr))
		{
		isAnyMode = FALSE;
		didMatch = TRUE;
		} else {
		if (*pStr)
			{
			didMatch = TRUE;
			pStr++;
			}
		}
	} else {
	if ((*pPattern) == (*pStr))
		{
		didMatch = TRUE;
		if (*pPattern) pPattern++;
		if (*pStr) pStr++;
		} else {
		if (tmpPattern)
			{
			didMatch = TRUE;
			isAnyMode = TRUE;
			pPattern = tmpPattern;
			}
		}
	}

	if (!*pStr && !*pPattern) isEnd = TRUE;
	break;

 } /* switch */

 } while ((didMatch) && (!isEnd));

 return didMatch;
}


/* Compare a string to a pattern. Case-INSENSITIVE version.
 */
BOOL th_strcasematch(char *pStr, char *pPattern)
{
 BOOL didMatch, isAnyMode, isEnd;
 char *tmpPattern;

 /* Check given pattern and string */
 if (!pStr) return FALSE;
 if (!pPattern) return FALSE;

 /* Initialize */
 tmpPattern = NULL;
 didMatch = TRUE;
 isEnd = FALSE;
 isAnyMode = FALSE;
 
 /* Start comparision */
 do 
 {
 switch (*pPattern) {
 case '?':
	/* Any single character matches */
	if (*pStr)
		{
		pPattern++;
		pStr++;
		} else
		didMatch = FALSE;
	break;

 case '*':
 	pPattern++;
 	if (!*pPattern || (*pPattern == '?')) isEnd = TRUE;
 	isAnyMode = TRUE;
 	tmpPattern = pPattern;
 	break;

 case 0:
	if (isAnyMode)
	{
	if (*pStr)
		pStr++;
		else
		isEnd = TRUE;
	} else {
	if (*pStr)
		{
		if (tmpPattern)
			{
			isAnyMode = TRUE;
			pPattern = tmpPattern;
			} else
			didMatch = FALSE;
		} else
		isEnd = TRUE;
	}
 	break;
 	
 default:
	if (isAnyMode)
	{
	if (th_tolower(*pPattern) == th_tolower(*pStr))
		{
		isAnyMode = FALSE;
		} else {
		if (*pStr)
			pStr++;
			else
			didMatch = FALSE;
		}
	} else {
	if (th_tolower(*pPattern) == th_tolower(*pStr))
		{
		if (*pPattern) pPattern++;
		if (*pStr) pStr++;
		} else {
		if (tmpPattern)
			{
			isAnyMode = TRUE;
			pPattern = tmpPattern;
			} else
			didMatch = FALSE;
		}
	}

	if (!*pStr && !*pPattern) isEnd = TRUE;
	break;

 } /* switch */
 
 } while ((didMatch) && (!isEnd));

 return didMatch;
}


/*
 * Handling of string-lists and hashes
 */
t_str_node *th_strnode_new(char *pcStr, t_ulint nUsed, void *pData)
{
 t_str_node *pResult;

 /* Allocate memory for new node */
 pResult = (t_str_node *) calloc(1, sizeof(t_str_node));
 if (!pResult) return NULL;

 /* Set fields */
 th_strcpy(&pResult->pcStr, pcStr);
 pResult->nUsed = nUsed;
 pResult->pData = pData;

 return pResult;
}


void th_strnode_free(t_str_node *pNode)
{
 assert(pNode);

 free(pNode->pcStr);
 free(pNode);
}


/* Insert a new node into strlist
 */
void th_strlist_insert(t_str_node **strList, t_str_node *pNode)
{
 assert(strList);
 assert(pNode);
 
 /* Insert into linked list */ 
 if (*strList)
	{
	/* The first node's pPrev points to last node */
	LPREV = (*strList)->pPrev;	/* New node's prev = Previous last node */
	(*strList)->pPrev->pNext = pNode;	/* Previous last node's next = New node */
	(*strList)->pPrev = pNode;		/* New last node = New node */
	LNEXT = NULL;				/* But next is NULL! */
 	} else {
	(*strList) = pNode;			/* First node ... */
	LPREV = pNode;				/* ... it's also last */
	LNEXT = NULL;				/* But next is NULL! */
 	}

}


/* Free a given strlist
 */
void th_strlist_free(t_str_node *strList)
{
 t_str_node *pNode, *nNode;
 
 pNode = strList;
 while (pNode)
 	{
 	nNode = pNode->pNext;
 	th_strnode_free(pNode);
 	pNode = nNode;
 	}
}


/* Create a strIndex from strlist
 */
t_str_index *th_strlist_makeindex(t_str_node *strList)
{
 t_str_index *pResult;
 t_str_node *pCurr;
 t_ulint n;
 assert(strList);
 
 /* Computer number of nodes */
 for (n = 0, pCurr = strList; pCurr; pCurr = pCurr->pNext)
	n++;

 /* Check number of nodes */
 if (n == 0) return NULL;

 /* Allocate memory for index */
 pResult = (t_str_index *) calloc(1, sizeof(t_str_index));
 if (!pResult) return NULL;
 
 pResult->n = n;
 pResult->ppIndex = (t_str_node **) malloc(sizeof(t_str_node *) * n);
 if (!pResult->ppIndex)
 	{
 	free(pResult);
 	return NULL;
 	}

 /* Create the index */
 for (n = 0, pCurr = strList; pCurr && (n < pResult->n); pCurr = pCurr->pNext)
	pResult->ppIndex[n++] = pCurr;

 return pResult;
}


/* Insert a node into given strhash
 */
int th_strhash_insert(t_str_hash strHash, t_str_node *pNode, BOOL ignoreCase)
{
 int i;
 assert(strHash);
 assert(pNode);
 assert(pNode->pcStr);
 
 if (ignoreCase)
	i = th_tolower(pNode->pcStr[0]);
	else
	i = pNode->pcStr[0];
 
 /* Check the hashcode */
 if ((i < 0) && (i >= SET_HASH_MAXINDEX))
 	return -1;
 
 if (strHash[i])
	{
	/* The first node's pPrev points to last node */
	pNode->pPrev = strHash[i]->pPrev;	/* New node's prev = Previous last node */
	strHash[i]->pPrev->pNext = pNode;	/* Previous last node's next = New node */
	strHash[i]->pPrev = pNode;		/* New last node = New node */
	pNode->pNext = NULL;			/* But next is NULL! */
 	} else {
	strHash[i] = pNode;			/* First node */
	pNode->pPrev = pNode;			/* But also last */
	pNode->pNext = NULL;			/* But next is NULL! */
 	}
 
 return 0; 
}


/* Free a given strhash
 */
void th_strhash_free(t_str_hash strHash)
{
 int i;
 assert(strHash);
 
 for (i = 0; i < SET_HASH_MAXINDEX; i++)
 	th_strlist_free(strHash[i]);
}


/* Change pData for matching entries to new value
 */
void th_strhash_change_pdata(t_str_hash strHash, void *pFind, void *pNew)
{
 t_str_node *pCurr;
 int i;
 assert(strHash);
 
 for (i = 0; i < SET_HASH_MAXINDEX; i++)
	{
 	/* Find from linked list */
	pCurr = strHash[i];
	while (pCurr)
		{
		if (pCurr->pData == pFind)
			pCurr->pData = pNew;

		pCurr = pCurr->pNext;
		}
	}
}


/* Search a string from a given stringhash, either case-sensitive or insensitive
 */
t_str_node *th_strhash_search(t_str_hash strHash, char *findStr, BOOL ignoreCase)
{
 t_str_node *pCurr;
 int i;
 BOOL isFound;
 assert(strHash);
 assert(findStr);

 isFound = FALSE;
 pCurr = NULL;
 
 /* Check hashcode */
 if (ignoreCase)
	i = th_tolower(findStr[0]);
	else
	i = findStr[0];
	
 if ((i < 0) && (i >= SET_HASH_MAXINDEX))
 	return NULL;
 
 /* Find from linked list */
 pCurr = strHash[i];
	
 if (ignoreCase)
	{
	/* Case in-sensitive search */
	while (pCurr && !isFound)
		{
		if (th_strcasecmp(findStr, pCurr->pcStr) == 0)
			isFound = TRUE;
			else
			pCurr = pCurr->pNext;
		}
	} else {
	/* Case sensitive search */
	while (pCurr && !isFound)
		{
		if (strcmp(findStr, pCurr->pcStr) == 0)
			isFound = TRUE;
			else
			pCurr = pCurr->pNext;
		}
	}
 
 /* Return result */
 if (isFound)
	return pCurr;
	else
	return NULL;
}


/* Create a strIndex from strHash
 */
t_str_index *th_strhash_makeindex(t_str_hash strHash)
{
 t_str_index *pResult;
 t_str_node *pCurr;
 t_uint n, i;
 assert(strHash);
 
 /* Computer number of nodes */
 for (n = i = 0; i < SET_HASH_MAXINDEX; i++)
 	{
 	pCurr = strHash[i];
 	while (pCurr)
 		{
 		n++;
 		pCurr = pCurr->pNext;
 		}
 	}

 /* Check number of nodes */
 if (n <= 0) return NULL;

 /* Allocate memory for index */
 pResult = (t_str_index *) calloc(1, sizeof(t_str_index));
 if (!pResult) return NULL;
 
 pResult->n = n;
 pResult->ppIndex = (t_str_node **) malloc(sizeof(t_str_node *) * n);
 if (!pResult->ppIndex)
 	{
 	free(pResult);
 	return NULL;
 	}

 /* Create the index */
 for (n = i = 0; (i < SET_HASH_MAXINDEX) && (n < pResult->n); i++)
 	{
 	pCurr = strHash[i];
 	while (pCurr && (n < pResult->n))
 		{
 		pResult->ppIndex[n++] = pCurr;
 		pCurr = pCurr->pNext;
 		}
 	}

 return pResult;
}


/* Free a given strIndex
 */
void th_strindex_free(t_str_index *strIndex)
{
 if (strIndex)
 	{
 	free(strIndex->ppIndex);
 	free(strIndex);
 	}
}


/* Compare two t_str_nodes by nUsed
 */
int th_strindex_cmp_used(const void *pNode1, const void *pNode2)
{
 t_str_node *pStr1, *pStr2;

 pStr1 = * (t_str_node **) pNode1;
 pStr2 = * (t_str_node **) pNode2;

 if (pStr1->nUsed > pStr2->nUsed)
 	return -1;
 	else
 if (pStr1->nUsed < pStr2->nUsed)
 	return 1;
 	else
 	return 0;
}


/* Sort an strIndex by nUsed, using th_strindex_cmp_used()
 */
void th_strindex_sort_nused(t_str_index *strIndex)
{
 assert(strIndex);
 assert(strIndex->ppIndex);

 qsort(strIndex->ppIndex, strIndex->n, sizeof(t_str_node *), th_strindex_cmp_used);
}


/* Compare two t_str_nodes via strcmp()
 */
int th_strindex_cmp_alpha(const void *pNode1, const void *pNode2)
{
 t_str_node *pStr1, *pStr2;

 pStr1 = * (t_str_node **) pNode1;
 pStr2 = * (t_str_node **) pNode2;

 return strcmp(pStr1->pcStr, pStr2->pcStr);
}


/* Sort an strIndex by nUsed, using th_strindex_cmp_used()
 */
void th_strindex_sort_alpha(t_str_index *strIndex)
{
 assert(strIndex);
 assert(strIndex->ppIndex);

 qsort(strIndex->ppIndex, strIndex->n, sizeof(t_str_node *), th_strindex_cmp_alpha);
}




syntax highlighted by Code2HTML, v. 0.9.1