view src/xs_stil.c @ 214:575686094eb1

Portability fixes
author Matti Hamalainen <ccr@tnsp.org>
date Wed, 15 Dec 2004 11:02:02 +0000
parents 7327a985aee8
children 16e3b2446a73
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 <ctype.h>


/*
 * Pointer to database in memory
 */
static t_xs_stil_node	*xs_stildb = NULL;
static t_xs_stil_node	**xs_stildbi = NULL;
static gint		xs_stildbn   = 0;


/*
 * Hash-database handling functions
 */
t_xs_stil_node *xs_stildb_node_new(void)
{
 t_xs_stil_node *pResult;
 
 /* Allocate memory for new node */
 pResult = (t_xs_stil_node *) g_malloc0(sizeof(t_xs_stil_node));
 return pResult;
}


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
 */
#define LPREV	(pNode->pPrev)
#define LTHIS	(pNode)
#define LNEXT	(pNode->pNext)

void xs_stildb_node_insert(t_xs_stil_node *pNode)
{
 if (xs_stildb)
 	{
 	/* The first node's pPrev points to last node */
	LPREV = xs_stildb->pPrev;		/* New node's prev = Previous last node */
	xs_stildb->pPrev->pNext = pNode;	/* Previous last node's next = New node */
	xs_stildb->pPrev = pNode;		/* New last node = New node */
	LNEXT = NULL;				/* But next is NULL! */
 	} else {
 	xs_stildb = pNode;			/* First node ... */
 	LPREV = pNode;				/* ... it's also last */
 	LNEXT = NULL;				/* But next is NULL! */
 	}
}

gint xs_strmcat(gchar **ppResult, gchar *pStr)
{
 assert(ppResult);

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

 if (*ppResult != NULL)
 	{
 	*ppResult = (gchar *) g_realloc(*ppResult, strlen(*ppResult) + strlen(pStr) + 1);
	if (*ppResult == NULL) return -1;
	strcat(*ppResult, pStr);
	} else
	{
	*ppResult = (gchar *) g_malloc(strlen(pStr) + 1);
	if (*ppResult == NULL) return -1;
	strcpy(*ppResult, pStr);
	}

 return 0;
}


/*
 * Read database to memory
 */
gint xs_stildb_read(gchar *dbFilename)
{
 FILE *inFile;
 gchar inLine[XS_BUFSIZE + 10];
 guint lineNum, linePos, eolPos;
 t_xs_stil_node *tmpNode;
 gboolean isError;
 gint subEntry;
 
 /* 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 */
 	tmpNode = xs_stildb_node_new();
 	if (tmpNode)
 		{
 		/* Initialize the node */
 		tmpNode->pcFilename = g_strdup(inLine);
	 	subEntry = 0;
	 	} else
 		{
 		/* 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(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_strmcat(&(tmpNode->subTune[subEntry].pInfo), &inLine[2]);
		} else
 	if (strncmp(inLine, " ARTIST:", 8) == 0)
 		{
 		inLine[eolPos++] = '\n';
 		inLine[eolPos++] = 0;
 		xs_strmcat(&(tmpNode->subTune[subEntry].pInfo), &inLine[1]);
 		} else
 	if (strncmp(inLine, "COMMENT:", 8) == 0)
 		xs_strmcat(&(tmpNode->subTune[subEntry].pInfo), inLine);
 		else
 	if (strncmp(inLine, "        ", 8) == 0)
 		xs_strmcat(&(tmpNode->subTune[subEntry].pInfo), &inLine[8]);
 	break;
 }
 
 } /* while */

 /* Check if there is one remaining node */
 if (tmpNode)
	xs_stildb_node_free(tmpNode);

 /* Close the file */  
 fclose(inFile);
 return 0;
}


/*
 * Compare two nodes' hashes
 */
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);
}


/*
 * Initialize the song-length system
 */
gint xs_stil_init(void)
{
 t_xs_stil_node *pCurr;
 gint i;

XSDEBUG("stil_init()\n");
 
 /* Read the database */
 if (!xs_cfg.stilDBPath)
 	return -10;

 if (xs_stildb_read(xs_cfg.stilDBPath) < 0)
 	return -9;

XSDEBUG("indexing...\n");

 /* Get size of db */
 pCurr = xs_stildb;
 xs_stildbn = 0;
 while (pCurr)
 	{
 	xs_stildbn++;
 	pCurr = pCurr->pNext;
 	}

 /* Check number of nodes */
 if (xs_stildbn > 0)
	{
	/* Allocate memory for index-table */
	xs_stildbi = (t_xs_stil_node **) g_malloc(sizeof(t_xs_stil_node *) * xs_stildbn);
	if (!xs_stildbi) return -6;

	/* Get node-pointers to table */
	i = 0;
	pCurr = xs_stildb;
	while (pCurr)
		{
		xs_stildbi[i++] = pCurr;
		pCurr = pCurr->pNext;
		}

	/* Sort the indexes */
	qsort(xs_stildbi, xs_stildbn, sizeof(t_xs_stil_node *), xs_stildb_cmp);
	}

 /* OK */
XSDEBUG("init ok.\n");
 return 0;
}


/*
 * Close song-length system
 */
void xs_stil_close(void)
{
 t_xs_stil_node *pCurr, *pNext;
 
 /* Free the memory allocated for database */
XSDEBUG("stildb_close()\n");
 pCurr = xs_stildb;
 while (pCurr)
 	{
 	pNext = pCurr->pNext;
	xs_stildb_node_free(pCurr);
 	pCurr = pNext;
 	}

 xs_stildb = NULL;
 
 /* Free memory allocated for indexes */
 if (xs_stildbi)
 	{
 	g_free(xs_stildbi);
 	xs_stildbi = NULL;
 	}
}


/*
 * Get STIL information from database
 */
t_xs_stil_node * xs_stil_get(gchar *pcFilename)
{
 gint iStartNode, iEndNode, iQNode, r, i;
 gchar *tmpFilename;
 gboolean iFound;
 t_xs_stil_node *pResult;

 /* Check the database pointers */
 if (!xs_stildb || !xs_stildbi || !xs_cfg.stilDBEnable || !xs_cfg.hvscPath)
 	return NULL;
 
 /* Remove postfixed directory separator from HVSC-path */
 tmpFilename = 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; 

 XSDEBUG("'%s', '%s'\n", xs_cfg.hvscPath, tmpFilename);
 	
 /* Look-up via index using binary search */
 pResult = NULL;
 iStartNode = 0;
 iEndNode = (xs_stildbn - 1);
 iQNode = (iEndNode / 2);
 iFound = FALSE;

 while ((!iFound) && ((iEndNode - iStartNode) > 128))
 	{
 	r = strcmp(tmpFilename, xs_stildbi[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(tmpFilename, xs_stildbi[i]->pcFilename) == 0)
			iFound = TRUE;
			else
			i++;
		}
	
	/* Check the result */
	if (iFound)
		pResult = xs_stildbi[i];

 	} else {
	/* Found via binary search */
 	pResult = xs_stildbi[iQNode];
	}

 return pResult;
}