view src/xs_stil.c @ 230:608f31f6c095

Raw cleanup.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 21 Dec 2004 09:25:03 +0000
parents 92bad4c7b998
children e613873c3379
line wrap: on
line source

/*  
   XMMS-SID - SIDPlay input plugin for X MultiMedia System (XMMS)

   STIL-database handling functions
   
   Written by Matti "ccr" Hamalainen <ccr@tnsp.org>

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "xs_stil.h"
#include "xs_support.h"
#include "xs_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>


/* Database handling functions
 */
static t_xs_stil_node *xs_stildb_node_new(gchar *pcFilename)
{
 t_xs_stil_node *pResult;
 
 /* Allocate memory for new node */
 pResult = (t_xs_stil_node *) g_malloc0(sizeof(t_xs_stil_node));
 if (!pResult) return NULL;

 pResult->pcFilename = g_strdup(pcFilename);
 if (!pResult->pcFilename)
 	{
 	g_free(pResult);
 	return NULL;
 	}

 return pResult;
}


static void xs_stildb_node_free(t_xs_stil_node *pNode)
{
 gint i;
 
 if (pNode)
 	{
 	/* Free subtune information */
 	for (i = 0; i < XS_STIL_MAXENTRY; i++)
 		{
 		g_free(pNode->subTune[i].pName);
 		g_free(pNode->subTune[i].pAuthor);
 		g_free(pNode->subTune[i].pInfo);
 		}
 	
 	g_free(pNode->pcFilename);
 	g_free(pNode);
 	}
}


/* Insert given node to db linked list
 */
static void xs_stildb_node_insert(t_xs_stildb *db, t_xs_stil_node *pNode)
{
 assert(db);
 
 if (db->pNodes)
 	{
 	/* The first node's pPrev points to last node */
	LPREV = db->pNodes->pPrev;		/* New node's prev = Previous last node */
	db->pNodes->pPrev->pNext = pNode;	/* Previous last node's next = New node */
	db->pNodes->pPrev = pNode;		/* New last node = New node */
	LNEXT = NULL;				/* But next is NULL! */
 	} else {
 	db->pNodes = pNode;			/* First node ... */
 	LPREV = pNode;				/* ... it's also last */
 	LNEXT = NULL;				/* But next is NULL! */
 	}
}


/* Read database (additively) to given db-structure
 */
gint xs_stildb_read(t_xs_stildb *db, gchar *dbFilename)
{
 FILE *inFile;
 gchar inLine[XS_BUFSIZE + 10];
 guint lineNum, linePos, eolPos;
 t_xs_stil_node *tmpNode;
 gboolean isError;
 gint subEntry;
 assert(db);
 
 /* Try to open the file */
 if ((inFile = fopen(dbFilename, "ra")) == NULL)
 	{
 	XSERR("Could not open STILDB '%s'\n", dbFilename);
 	return -1;
 	}
 
 /* Read and parse the data */
 lineNum = 0;
 isError = FALSE;
 tmpNode = NULL;
 subEntry = 0;
  
 while (!feof(inFile) && !isError)
 {
 fgets(inLine, XS_BUFSIZE, inFile);
 linePos = eolPos = 0;
 xs_findeol(inLine, &eolPos);
 inLine[eolPos] = 0;
 lineNum++;

 switch (inLine[0]) {
 case '/':
 	/* Check if we are already parsing entry */
 	if (tmpNode)
 		{
 		XSERR("New entry ('%s') before end of current ('%s')! Possibly malformed STIL-file!\n",
 			inLine, tmpNode->pcFilename);

 		xs_stildb_node_free(tmpNode);
 		}
 
 	/* A new node */
 	subEntry = 0;
 	tmpNode = xs_stildb_node_new(inLine);
 	if (!tmpNode)
 		{
 		/* Allocation failed */
 		XSERR("Could not allocate new STILdb-node for '%s'!\n", inLine);
 		isError = TRUE;
 		}
	break;

 case '(':
 	/* A new sub-entry */
 	linePos++;
 	if (inLine[linePos] == '#')
 		{
 		linePos++;
 		if (inLine[linePos])
 			{
		 	xs_findnum(inLine, &linePos);
		 	inLine[linePos] = 0;
	 		subEntry = atol(&inLine[2]);

		 	/* Sanity check */
		 	if ((subEntry < 1) || (subEntry >= XS_STIL_MAXENTRY))
 				{
 				XSERR("Number of subEntry (%i) for '%s' is invalid\n", subEntry, tmpNode->pcFilename);
 				subEntry = 0;
 				}
	 		}
	 	}
 	
 	break;

 case 0:
 case '#':
 case '\n':
 case '\r':
 	/* End of entry/field */
 	if (tmpNode)
 		{
 		/* Insert to database */
 		xs_stildb_node_insert(db, tmpNode);
		tmpNode = NULL;
		}
 	break;
 
 default:
 	/* Check if we are parsing an entry */
 	if (!tmpNode)
 		{
 		XSERR("Entry data encountered outside of entry!\n");
 		break;
 		}

 	/* Some other type */
 	if (strncmp(inLine, "   NAME:", 8) == 0)
 		{
 		g_free(tmpNode->subTune[subEntry].pName);
 		tmpNode->subTune[subEntry].pName = g_strdup(&inLine[9]);
 		} else
 	if (strncmp(inLine, " AUTHOR:", 8) == 0)
 		{
 		g_free(tmpNode->subTune[subEntry].pAuthor);
 		tmpNode->subTune[subEntry].pAuthor = g_strdup(&inLine[9]);
 		} else
 	if (strncmp(inLine, "  TITLE:", 8) == 0)
 		{
 		inLine[eolPos++] = '\n';
 		inLine[eolPos++] = 0;
 		xs_pstrcat(&(tmpNode->subTune[subEntry].pInfo), &inLine[2]);
		} else
 	if (strncmp(inLine, " ARTIST:", 8) == 0)
 		{
 		inLine[eolPos++] = '\n';
 		inLine[eolPos++] = 0;
 		xs_pstrcat(&(tmpNode->subTune[subEntry].pInfo), &inLine[1]);
 		} else
 	if (strncmp(inLine, "COMMENT:", 8) == 0)
 		xs_pstrcat(&(tmpNode->subTune[subEntry].pInfo), inLine);
 		else
 	if (strncmp(inLine, "        ", 8) == 0)
 		xs_pstrcat(&(tmpNode->subTune[subEntry].pInfo), &inLine[8]);
 	break;
 }
 
 } /* while */

 /* Check if there is one remaining node */
 if (tmpNode)
	xs_stildb_node_insert(db, tmpNode);

 /* Close the file */  
 fclose(inFile);

 return 0;
}


/* Compare two nodes
 */
static gint xs_stildb_cmp(const void *pNode1, const void *pNode2)
{
 /* We assume here that we never ever get NULL-pointers or similar */
 return strcmp((*(t_xs_stil_node **) pNode1)->pcFilename,
 		(*(t_xs_stil_node **) pNode2)->pcFilename);
}


/* (Re)create index
 */
gint xs_stildb_index(t_xs_stildb *db)
{
 t_xs_stil_node *pCurr;
 gint i;

 /* Free old index */
 if (db->ppIndex)
 	{
 	g_free(db->ppIndex);
 	db->ppIndex = NULL;
 	}
 
 /* Get size of db */
 pCurr = db->pNodes;
 db->n = 0;
 while (pCurr)
 	{
 	db->n++;
 	pCurr = pCurr->pNext;
 	}

 /* Check number of nodes */
 if (db->n > 0)
	{
	/* Allocate memory for index-table */
	db->ppIndex = (t_xs_stil_node **) g_malloc(sizeof(t_xs_stil_node *) * db->n);
	if (!db->ppIndex) return -1;

	/* Get node-pointers to table */
	i = 0;
	pCurr = db->pNodes;
	while (pCurr && (i < db->n))
		{
		db->ppIndex[i++] = pCurr;
		pCurr = pCurr->pNext;
		}

	/* Sort the indexes */
	qsort(db->ppIndex, db->n, sizeof(t_xs_stil_node *), xs_stildb_cmp);
	}

 return 0;
}

/* Free a given STIL database
 */
void xs_stildb_free(t_xs_stildb *db)
{
 t_xs_stil_node *pCurr, *pNext;
 
 if (!db) return;
 
 /* Free the memory allocated for nodes */
 pCurr = db->pNodes;
 while (pCurr)
 	{
 	pNext = pCurr->pNext;
	xs_stildb_node_free(pCurr);
 	pCurr = pNext;
 	}

 db->pNodes = NULL;
 
 /* Free memory allocated for index */
 if (db->ppIndex)
 	{
 	g_free(db->ppIndex);
 	db->ppIndex = NULL;
 	}

 /* Free structure */
 db->n = 0;
 g_free(db);
}


/* Get STIL information node from database
 */
static t_xs_stil_node * xs_stildb_get_node(t_xs_stildb *db, gchar *pcFilename)
{
 gint iStartNode, iEndNode, iQNode, r, i;
 gboolean iFound;
 t_xs_stil_node *pResult;

 /* Check the database pointers */
 if (!db || !db->pNodes || !db->ppIndex)
 	return NULL;
 
 /* Look-up via index using binary search */
 pResult = NULL;
 iStartNode = 0;
 iEndNode = (db->n - 1);
 iQNode = (iEndNode / 2);
 iFound = FALSE;

 while ((!iFound) && ((iEndNode - iStartNode) > 128))
 	{
 	r = strcmp(pcFilename, db->ppIndex[iQNode]->pcFilename);
 	if (r < 0)
 		{
 		/* Hash was in the <- LEFT side */
 		iEndNode = iQNode;
 		iQNode = iStartNode + ((iEndNode - iStartNode) / 2);
 		} else
 	if (r > 0)
 		{
 		/* Hash was in the RIGHT -> side */
 		iStartNode = iQNode;
 		iQNode = iStartNode + ((iEndNode - iStartNode) / 2);
 		} else
 		iFound = TRUE;
 	}

 /* If not found already */
 if (!iFound)
 	{
	/* Search the are linearly */
	iFound = FALSE;
	i = iStartNode;
	while ((i <= iEndNode) && (!iFound))
		{
		if (strcmp(pcFilename, db->ppIndex[i]->pcFilename) == 0)
			iFound = TRUE;
			else
			i++;
		}
	
	/* Check the result */
	if (iFound)
		pResult = db->ppIndex[i];

 	} else {
	/* Found via binary search */
 	pResult = db->ppIndex[iQNode];
	}

 return pResult;
}


/*
 * These should be moved out of this module some day ...
 */
static t_xs_stildb	*xs_stildb_db = NULL;
XS_MUTEX(xs_stildb_db);

gint xs_stil_init(void)
{
 XS_MUTEX_LOCK(xs_cfg);

 if (!xs_cfg.stilDBPath)
 	{
 	XS_MUTEX_UNLOCK(xs_cfg);
 	return -1;
 	}

 XS_MUTEX_LOCK(xs_stildb_db);
 
 /* Check if already initialized */
 if (xs_stildb_db) xs_stildb_free(xs_stildb_db);
 
 /* Allocate database */
 xs_stildb_db = (t_xs_stildb *) g_malloc0(sizeof(t_xs_stildb));
 if (!xs_stildb_db)
 	{
 	XS_MUTEX_UNLOCK(xs_cfg);
 	XS_MUTEX_UNLOCK(xs_stildb_db);
 	return -2;
 	}
 
 /* Read the database */
 if (xs_stildb_read(xs_stildb_db, xs_cfg.stilDBPath) != 0)
 	{
 	xs_stildb_free(xs_stildb_db);
 	xs_stildb_db = NULL;
 	XS_MUTEX_UNLOCK(xs_cfg);
 	XS_MUTEX_UNLOCK(xs_stildb_db);
 	return -3;
 	}

 /* Create index */
 if (xs_stildb_index(xs_stildb_db) != 0)
 	{
 	xs_stildb_free(xs_stildb_db);
 	xs_stildb_db = NULL;
 	XS_MUTEX_UNLOCK(xs_cfg);
 	XS_MUTEX_UNLOCK(xs_stildb_db);
 	return -4;
 	}

 XS_MUTEX_UNLOCK(xs_cfg);
 XS_MUTEX_UNLOCK(xs_stildb_db);
 return 0;
}


void xs_stil_close(void)
{
 XS_MUTEX_LOCK(xs_stildb_db);
 xs_stildb_free(xs_stildb_db);
 xs_stildb_db = NULL;
 XS_MUTEX_UNLOCK(xs_stildb_db);
}


t_xs_stil_node * xs_stil_get(gchar *pcFilename)
{
 t_xs_stil_node *pResult;
 gchar *tmpFilename;

 XS_MUTEX_LOCK(xs_stildb_db);
 XS_MUTEX_LOCK(xs_cfg);

 if (xs_cfg.hvscPath)
 	{
 	/* Remove postfixed directory separator from HVSC-path */
 	tmpFilename = xs_strrchr(xs_cfg.hvscPath, '/');
 	if (tmpFilename && (tmpFilename[1] == 0))
 		tmpFilename[0] = 0;
 
	/* Remove HVSC location-prefix from filename */
	tmpFilename = strstr(pcFilename, xs_cfg.hvscPath);
	if (tmpFilename)
		tmpFilename += strlen(xs_cfg.hvscPath);
		else
		tmpFilename = pcFilename; 
	} else
	tmpFilename = pcFilename;
	
 pResult = xs_stildb_get_node(xs_stildb_db, tmpFilename);

 XS_MUTEX_UNLOCK(xs_stildb_db);
 XS_MUTEX_UNLOCK(xs_cfg);
 
 return pResult;
}