/*
* 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