/*
 * FISG - User- and nick-related datastructure handling
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2003-2004 Tecnic Software productions (TNSP)
 *
 * Please read file 'COPYING' for information on license and distribution.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "fisg.h"
#include "th_util.h"
#include "th_string.h"

/*
 * Userlist handling
 */
/* Allocate new t_user_data structure
 */
t_user_data *user_data_new(void)
{
 t_user_data *pData;
 
 /* Allocate the structure */
 pData = (t_user_data *) calloc(1, sizeof(t_user_data));
 if (!pData) return NULL;

 return pData;
}


/* Free t_user_data structure
 */
void user_data_free(t_user_data *pData)
{
 t_user_entry *pCurr, *pNext;
 
 if (pData)
 	{
 	/* Free linked list */
 	pCurr = pData->pList;
 	while (pCurr)
 		{
 		pNext = pCurr->pNext;
 		user_free(pCurr); 		
 		pCurr = pNext;
 		}
 	
 	/* Free index */
 	free(pData->ppIndex); 	
 	
 	/* Clear structure */
 	pData->pList = NULL;
 	pData->ppIndex = NULL;
 	pData->dirtyN =
	pData->dirtyIndex = FALSE;
 	
 	/* Free structure */
 	free(pData);
 	}
}


/* Allocate a new user node
 */
t_user_entry *user_new(char *userHandle)
{
 t_user_entry *pResult;

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

 /* Set fields */
 th_strcpy(&pResult->userHandle, userHandle);

 return pResult;
}


/* Free user node and data
 */
void user_free(t_user_entry *pUser)
{
 assert(pUser);

 if (pUser->userHandle) free(pUser->userHandle);
 if (pUser->picPath) free(pUser->picPath);
 if (pUser->linkURL) free(pUser->linkURL);
 if (pUser->sComment) free(pUser->sComment);

 free(pUser);
}


/* Insert given node to linked list
 */
void user_insert(t_user_data *pData, t_user_entry *pNode)
{
 assert(pData);
 assert(pNode);
 
 /* Make dirty */
 pData->dirtyN = pData->dirtyIndex = TRUE;
 
 /* Insert node */
 if (pData->pList)
	{
	/* The first node's pPrev points to last node */
	LPREV = pData->pList->pPrev;	/* New node's prev = Previous last node */
	pData->pList->pPrev->pNext = pNode;	/* Previous last node's next = New node */
	pData->pList->pPrev = pNode;		/* New last node = New node */
	LNEXT = NULL;				/* But next is NULL! */
 	} else {
	pData->pList = pNode;			/* First node ... */
	LPREV = pNode;				/* ... it's also last */
	LNEXT = NULL;				/* But next is NULL! */
 	}
}


/* Delete given user node from linked list
 */
void user_delete(t_user_data *pData, t_user_entry *pNode)
{
 assert(pData);
 assert(pData->pList);
 assert(pNode);

 /* Make dirty */
 pData->dirtyN = pData->dirtyIndex = TRUE;
 
 /* Delete node */
 if (LPREV) LPREV->pNext = LNEXT;
 if (LNEXT)
	LNEXT->pPrev = LPREV;
	else
	pData->pList->pPrev = LPREV;

 LPREV = LNEXT = NULL;
}


/*
 * User data structures handling
 */
/* Compare function for qsort() that compares 2 nodes for fTotalScore
 */
int user_data_index_cmp(const void *pNode1, const void *pNode2)
{
 t_user_entry *pUser1, *pUser2;

 pUser1 = * (t_user_entry **) pNode1;
 pUser2 = * (t_user_entry **) pNode2;

 if (pUser1->fTotalScore > pUser2->fTotalScore)
 	return -1;
 	else
 if (pUser1->fTotalScore < pUser2->fTotalScore)
 	return 1;
 	else
 	return 0;
}


/* Create a user index for given t_user_data structure
 * from nodes in the linked list.
 */
int user_data_makeindex(t_user_data *pData)
{
 t_user_entry *pCurr;
 t_ulint i;
 assert(pData);
 
 /* Free old index, if it exists */
 free(pData->ppIndex);
 pData->ppIndex = NULL;
 
 /* Computer number of nodes */
 for (pData->n = 0, pCurr = pData->pList; pCurr; pCurr = pCurr->pNext)
	pData->n++;

 pData->dirtyN = FALSE;
 
 /* Check number of nodes */
 if (pData->n == 0) return 0;

 /* Allocate memory for index */
 pData->ppIndex = (t_user_entry **) malloc(sizeof(t_user_entry *) * pData->n);
 if (pData->ppIndex == NULL)
 	return -1;

 /* Copy node-pointers to the index-table */
 i = 0;
 pCurr = pData->pList;
 while (pCurr && (i < pData->n))
	{
	pData->ppIndex[i++] = pCurr;
	pCurr = pCurr->pNext;
	}

 if (i != pData->n) return -2;

 pData->dirtyIndex = FALSE;
 
 return 0;
}


/*
 * Nicklist/hash handling
 */
/* Insert given node into the nicklist/hashtable
 */
int nickhash_insert(t_str_hash nickList, t_str_node *pNode)
{
 int i;
 char *tmpNick;
 assert(nickList);
 assert(pNode);
 assert(pNode->pcStr);
 
 /* Find first non-token character from nick */
 tmpNick = pNode->pcStr;
 while ((*tmpNick == '*') || (*tmpNick == '?')) tmpNick++;
 i = th_tolower(*tmpNick);
 
 /* Check the hash index */
 if ((i < 0) && (i >= SET_HASH_MAXINDEX))
 	return -1;
 
 /* Insert node in the linked list */
 if (nickList[i])
	{
	/* The first node's pPrev points to last node */
	pNode->pPrev = nickList[i]->pPrev;	/* New node's prev = Previous last node */
	nickList[i]->pPrev->pNext = pNode;	/* Previous last node's next = New node */
	nickList[i]->pPrev = pNode;		/* New last node = New node */
	pNode->pNext = NULL;			/* But next is NULL! */
 	} else {
	nickList[i] = pNode;			/* First node */
	pNode->pPrev = pNode;			/* But also last */
	pNode->pNext = NULL;			/* But next is NULL! */
 	}
 
 return 0;
}


/* Check if given nick is in the nicklist
 */
t_str_node *nickhash_search(t_str_hash nickList, char *findNick)
{
 t_str_node *pCurr;
 int i, j;
 BOOL isFound;

 assert(nickList);

 isFound = FALSE;
 pCurr = NULL;
 j = 0;
 
 /* Check hash */
 while ((!isFound) && (findNick[j]))
 {
 i = th_tolower(findNick[j]);
 if ((i >= 0) && (i < SET_HASH_MAXINDEX))
 	{
 	/* Find from linked list */
	pCurr = nickList[i];
	while (pCurr && (!isFound))
		{
		if (th_strcasematch(findNick, pCurr->pcStr))
			isFound = TRUE;
			else
			pCurr = pCurr->pNext;
		}
	}
 j++;
 }
 
 /* Return result */
 if (isFound)
	return pCurr;
	else
	return NULL;
}


/*
 * Userfile parser
 */
/* Get a field ending to line-end or colon ':'
 */
void parse_pfield1(char *inLine, char *tmpBuf, size_t bufSize, size_t *linePos)
{
 size_t i = 0;

 while (inLine[*linePos] && (inLine[*linePos] != ':') && (i < bufSize))
	tmpBuf[i++] = inLine[(*linePos)++];

 tmpBuf[i++] = 0;
}


/* Get a field ending to line-end, whitespace or colon ':'
 */
void parse_pfield2(char *inLine, char *tmpBuf, size_t bufSize, size_t *linePos)
{
 size_t i = 0;

 while (inLine[*linePos] && !th_isspace(inLine[*linePos]) && (inLine[*linePos] != ':') && (i < bufSize))
	tmpBuf[i++] = inLine[(*linePos)++];

 tmpBuf[i++] = 0;
}


/* Get a field ending to line-end or whitespace
 */
void parse_pfield3(char *inLine, char *tmpBuf, size_t bufSize, size_t *linePos)
{
 size_t i = 0;

 while (inLine[*linePos] && !th_isspace(inLine[*linePos]) && (i < bufSize))
	tmpBuf[i++] = inLine[(*linePos)++];

 tmpBuf[i++] = 0;
}


/* Parse a given userfile to nick and user information
 */
int parse_userfile(char *userFilename, t_stats *pStats, t_fisgconfig *pCfg)
{
 char inLine[SET_MAX_BUF + 1], tmpStr[SET_MAX_BUF + 1];
 FILE *inFile;
 size_t lineNum, linePos;
 t_user_entry *tmpUser = NULL;
 t_str_node *tmpNick = NULL;
 BOOL isIgnored;

 assert(pStats);
 
 /* Get configuration options */

 /* Try to open the file */
 if ((inFile = fopen(userFilename, "ra")) == NULL)
	return -1;
 
 /* Read and parse the data */
 lineNum = 0;
 while (fgets(inLine, SET_MAX_BUF, inFile) != NULL)
 {
 linePos = 0;
 while (inLine[linePos] && !th_iscrlf(inLine[linePos])) linePos++;
 inLine[linePos] = 0;
 lineNum++;
 linePos = 0;

 /* Check if the line is OK and what type it is */ 
 if (inLine[0] && (inLine[linePos] != '#'))
	{
	/* Check if it's ignored user */
	if (inLine[linePos] == '!')
		{
		linePos++;
		isIgnored = TRUE;
		} else
		isIgnored = FALSE;
	
	/* Get the user handle */
	parse_pfield1(inLine, tmpStr, SET_MAX_BUF, &linePos);

	/* Check if next field exists */
	if (inLine[linePos] != ':')
		{
		THERR("Error in userfile '%s', line %i - missing fields.\n",
			userFilename, lineNum);
		} else {
		/* Allocate a new user and nick records */
		tmpUser = user_new(tmpStr);
		tmpUser->isIgnored = isIgnored;
		tmpUser->isManaged = TRUE;
		user_insert(pStats->usersList, tmpUser);
		
		/* Get alias nicks */
		linePos++;
		while (inLine[linePos] && (inLine[linePos] != ':'))
			{
			/* Get one nick */
			th_findnext(inLine, &linePos);
			parse_pfield2(inLine, tmpStr, SET_MAX_BUF, &linePos);
			th_findnext(inLine, &linePos);

			/* Add to user */
			tmpNick = th_strnode_new(tmpStr, 0, tmpUser);
			
			if (nickhash_insert(pStats->nickList, tmpNick) != 0)
			THERR("nickhash_insert() failed, hash: '%s'\n", tmpStr);
			}
		
		/* Check if image path is given */
		if (inLine[linePos] == ':')
			{
			/* Get image path */
			linePos++;
			if (inLine[linePos] != ':')
				{
				parse_pfield1(inLine, tmpStr, SET_MAX_BUF, &linePos);
				if (tmpStr[0])
					th_strcpy(&tmpUser->picPath, tmpStr);
				}
			
			if (inLine[linePos] == ':')
				{
				/* Get user URL */
				linePos++;
				th_findnext(inLine, &linePos);
				parse_pfield3(inLine, tmpStr, SET_MAX_BUF, &linePos);
				if (tmpStr[0])
					th_strcpy(&tmpUser->linkURL, tmpStr);
				}
			}
		}
 	}
 
 } /* if (fgets()) */
 
 return 0;
}



syntax highlighted by Code2HTML, v. 0.9.1