view src/xs_stil.c @ 395:b571000e1f8c

Use xs_memset() instead of memset()
author Matti Hamalainen <ccr@tnsp.org>
date Wed, 31 May 2006 10:51:00 +0000
parents b09d74eb71e6
children f997b79a7251
line wrap: on
line source

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

   STIL-database handling functions
   
   Programmed and designed by Matti 'ccr' Hamalainen <ccr@tnsp.org>
   (C) Copyright 1999-2005 Tecnic Software productions (TNSP)

   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>
#include <stdarg.h>


/* Database handling functions
 */
static gboolean xs_stildb_node_realloc(t_xs_stil_node *pNode, gint nsubTunes)
{
	if (!pNode) return FALSE;
	
	/* Re-allocate subTune structure if needed */
	if (nsubTunes >= pNode->nsubTunes) {
		pNode->subTunes =
			(t_xs_stil_subnode **) g_realloc(pNode->subTunes,
			(nsubTunes + 1) * sizeof(t_xs_stil_subnode **));

		if (!pNode->subTunes)
			return FALSE;
		
		/* Clear the newly allocated memory */
		xs_memset(&(pNode->subTunes[pNode->nsubTunes]), 0,
			(nsubTunes + 1 - pNode->nsubTunes) *
			sizeof(t_xs_stil_subnode **));
	}
			
	/* Allocate memory for subTune */
	if (!pNode->subTunes[nsubTunes]) {
		pNode->subTunes[nsubTunes] = (t_xs_stil_subnode *)
			g_malloc0(sizeof(t_xs_stil_subnode));
		
		if (!pNode->subTunes[nsubTunes])
			return FALSE;
	}

	return TRUE;
}


static void xs_stildb_node_free(t_xs_stil_node *pNode)
{
	gint i;
	t_xs_stil_subnode *pSub;

	if (pNode) {
		/* Free subtune information */
		for (i = 0; i < pNode->nsubTunes; i++) {
			pSub = pNode->subTunes[i];
			if (pSub) {
				g_free(pSub->pName);
				g_free(pSub->pAuthor);
				g_free(pSub->pInfo);

				g_free(pSub);
			}
		}
		
		g_free(pNode->subTunes);
		g_free(pNode->pcFilename);
		g_free(pNode);
	}
}


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;

	/* Allocate filename and initial space for one subtune */
	pResult->pcFilename = g_strdup(pcFilename);
	if (!pResult->pcFilename || !xs_stildb_node_realloc(pResult, 1)) {
		xs_stildb_node_free(pResult);
		return NULL;
	}
	
	return pResult;
}


/* 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
 */
#define XS_STILDB_MULTI							\
	if (isMulti) {							\
		isMulti = FALSE;					\
		xs_pstrcat(&(tmpNode->subTunes[subEntry]->pInfo), "\n");\
	}

void XS_STILDB_ERR(gint lineNum, gchar *inLine, const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	xs_error(fmt, ap);
	va_end(ap);
	
	fprintf(stderr, "#%d: '%s'\n", lineNum, inLine);
}

gint xs_stildb_read(t_xs_stildb *db, gchar *dbFilename)
{
	FILE *inFile;
	gchar inLine[XS_BUF_SIZE + 16];	/* Since we add some chars here and there */
	guint lineNum, linePos, eolPos;
	t_xs_stil_node *tmpNode;
	gboolean isError, isMulti;
	gint subEntry;
	assert(db);

	/* Try to open the file */
	if ((inFile = fopen(dbFilename, "ra")) == NULL) {
		xs_error("Could not open STILDB '%s'\n", dbFilename);
		return -1;
	}

	/* Read and parse the data */
	lineNum = 0;
	isError = FALSE;
	isMulti = FALSE;
	tmpNode = NULL;
	subEntry = 0;

	while (!feof(inFile) && !isError) {
		fgets(inLine, XS_BUF_SIZE, inFile);
		inLine[XS_BUF_SIZE - 1] = 0;
		linePos = eolPos = 0;
		xs_findeol(inLine, &eolPos);
		inLine[eolPos] = 0;
		lineNum++;

		switch (inLine[0]) {
		case '/':
			/* Check if we are already parsing entry */
			isMulti = FALSE;
			if (tmpNode) {
				XS_STILDB_ERR(lineNum, inLine,
					"New entry found before end of current ('%s')!\n",
					tmpNode->pcFilename);
				xs_stildb_node_free(tmpNode);
			}

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

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

					/* Sanity check */
					if (subEntry < 1) {
						XS_STILDB_ERR(lineNum, inLine,
							"Number of subEntry (%i) for '%s' is invalid\n",
							subEntry, tmpNode->pcFilename);
						subEntry = 0;
					}
				}
			} else {
				XS_STILDB_ERR(lineNum, inLine,
					"Syntax error, expected subEntry number.\n");
			}

			break;

		case 0:
		case '#':
		case '\n':
		case '\r':
			/* End of entry/field */
			isMulti = FALSE;
			if (tmpNode) {
				/* Insert to database */
				xs_stildb_node_insert(db, tmpNode);
				tmpNode = NULL;
			}
			break;

		default:
			/* Check if we are parsing an entry */
			if (!tmpNode) {
				XS_STILDB_ERR(lineNum, inLine,
					"Entry data encountered outside of entry or syntax error!\n");
				break;
			}

			if (xs_stildb_node_realloc(tmpNode, subEntry)) {
				XS_STILDB_ERR(lineNum, inLine,
					"Could not (re)allocate memory for subEntries!\n");
				isError = TRUE;
				break;
			}
			
			/* Some other type */
			if (strncmp(inLine, "   NAME:", 8) == 0) {
				XS_STILDB_MULTI;
				g_free(tmpNode->subTunes[subEntry]->pName);
				tmpNode->subTunes[subEntry]->pName = g_strdup(&inLine[9]);
			} else if (strncmp(inLine, " AUTHOR:", 8) == 0) {
				XS_STILDB_MULTI;
				g_free(tmpNode->subTunes[subEntry]->pAuthor);
				tmpNode->subTunes[subEntry]->pAuthor = g_strdup(&inLine[9]);
			} else if (strncmp(inLine, "  TITLE:", 8) == 0) {
				XS_STILDB_MULTI;
				inLine[eolPos++] = '\n';
				inLine[eolPos++] = 0;
				xs_pstrcat(&(tmpNode->subTunes[subEntry]->pInfo), &inLine[2]);
			} else if (strncmp(inLine, " ARTIST:", 8) == 0) {
				XS_STILDB_MULTI;
				inLine[eolPos++] = '\n';
				inLine[eolPos++] = 0;
				xs_pstrcat(&(tmpNode->subTunes[subEntry]->pInfo), &inLine[1]);
			} else if (strncmp(inLine, "COMMENT:", 8) == 0) {
				XS_STILDB_MULTI;
				isMulti = TRUE;
				xs_pstrcat(&(tmpNode->subTunes[subEntry]->pInfo), inLine);
			} else if (strncmp(inLine, "        ", 8) == 0) {
				if (isMulti) {
					xs_pstrcat(&(tmpNode->subTunes[subEntry]->pInfo), &inLine[8]);
				} else {
					XS_STILDB_ERR(lineNum, inLine,
					"Entry continuation found when isMulti == FALSE.\n");
				}
			}
			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) > XS_BIN_BAILOUT)) {
		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.stilDBEnable && xs_stildb_db) {
		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);
	} else
		pResult = NULL;

	XS_MUTEX_UNLOCK(xs_stildb_db);
	XS_MUTEX_UNLOCK(xs_cfg);

	return pResult;
}