view src/xs_stil.c @ 657:acaba070cf49

Lots of cosmetic code cleanups; synced the de-gettextification from Audacious-SID, I suppose it makes some sense ...
author Matti Hamalainen <ccr@tnsp.org>
date Wed, 02 Apr 2008 19:46:59 +0300
parents a8ceae9ae8e3
children b0743dc9165d
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-2007 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.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "xs_stil.h"
#include "xs_support.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdarg.h>


/* Database handling functions
 */
static gboolean xs_stildb_node_realloc(stil_node_t *node, gint nsubTunes)
{
	if (!node) return FALSE;

	/* Re-allocate subTune structure if needed */
	if (nsubTunes > node->nsubTunes) {
		gint clearIndex, clearLength;
		
		node->subTunes =
			(stil_subnode_t **) g_realloc(node->subTunes,
			(nsubTunes + 1) * sizeof(stil_subnode_t **));

		if (!node->subTunes) {
			xs_error("SubTune pointer structure realloc failed.\n");
			return FALSE;
		}
		
		/* Clear the newly allocated memory */
		if (node->nsubTunes == 0) {
			clearIndex = 0;
			clearLength = nsubTunes + 1;
		} else {
			clearIndex = node->nsubTunes + 1;
			clearLength = (nsubTunes - clearIndex + 1);
		}
		memset(&(node->subTunes[clearIndex]), 0, clearLength * sizeof(stil_subnode_t **));
		
		node->nsubTunes = nsubTunes;
	}

	/* Allocate memory for subTune */
	if (!node->subTunes[nsubTunes]) {
		node->subTunes[nsubTunes] = (stil_subnode_t *)
			g_malloc0(sizeof(stil_subnode_t));
		
		if (!node->subTunes[nsubTunes]) {
			xs_error("SubTune structure malloc failed!\n");
			return FALSE;
		}
	}

	return TRUE;
}


static void xs_stildb_node_free(stil_node_t *node)
{
	gint i;
	stil_subnode_t *subnode;

	if (!node) return;

	/* Free subtune information */
	for (i = 0; i <= node->nsubTunes; i++) {
		subnode = node->subTunes[i];
		if (subnode) {
			g_free(subnode->name);
			g_free(subnode->author);
			g_free(subnode->info);
			g_free(subnode->title);
			g_free(subnode);
		}
	}
	g_free(node->subTunes);
	g_free(node->filename);
	g_free(node);
}


static stil_node_t *xs_stildb_node_new(gchar *filename)
{
	stil_node_t *result;

	/* Allocate memory for new node */
	result = (stil_node_t *) g_malloc0(sizeof(stil_node_t));
	if (!result)
		return NULL;

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


/* Insert given node to db linked list
 */
static void xs_stildb_node_insert(xs_stildb_t *db, stil_node_t *node)
{
	assert(db != NULL);

	if (db->nodes) {
		/* The first node's pPrev points to last node */
		LPREV = db->nodes->prev;	/* New node's prev = Previous last node */
		db->nodes->prev->next = node;	/* Previous last node's next = New node */
		db->nodes->prev = node;	/* New last node = New node */
		LNEXT = NULL;	/* But next is NULL! */
	} else {
		db->nodes = node;	/* First node ... */
		LPREV = node;	/* ... 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(&(tmnode->subTunes[subEntry]->info), "\n");\
	}

static 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(xs_stildb_t *db, gchar *dbFilename)
{
	FILE *inFile;
	gchar inLine[XS_BUF_SIZE + 16];	/* Since we add some chars here and there */
	size_t lineNum;
	stil_node_t *tmnode;
	gboolean isError, isMulti;
	gint subEntry;
	gchar *tmpLine = inLine;
	assert(db != NULL);

	/* 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;
	tmnode = NULL;
	subEntry = 0;

	while (!isError && fgets(inLine, XS_BUF_SIZE, inFile) != NULL) {
		size_t linePos = 0, eolPos = 0;
		xs_findeol(inLine, &eolPos);
		inLine[eolPos] = 0;
		lineNum++;
		
		tmpLine = XS_CS_STIL(inLine);

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

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

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

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

			break;

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

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

			if (!xs_stildb_node_realloc(tmnode, subEntry)) {
				XS_STILDB_ERR(lineNum, tmpLine,
					"Could not (re)allocate memory for subEntries!\n");
				isError = TRUE;
				break;
			}
			
			/* Some other type */
			if (strncmp(tmpLine, "   NAME:", 8) == 0) {
				XS_STILDB_MULTI;
				g_free(tmnode->subTunes[subEntry]->name);
				tmnode->subTunes[subEntry]->name = g_strdup(&tmpLine[9]);
			} else if (strncmp(tmpLine, "  TITLE:", 8) == 0) {
				XS_STILDB_MULTI;
				isMulti = TRUE;
				if (!tmnode->subTunes[subEntry]->title)
					tmnode->subTunes[subEntry]->title = g_strdup(&tmpLine[9]);
				xs_pstrcat(&(tmnode->subTunes[subEntry]->info), &tmpLine[2]);
			} else if (strncmp(tmpLine, " AUTHOR:", 8) == 0) {
				XS_STILDB_MULTI;
				g_free(tmnode->subTunes[subEntry]->author);
				tmnode->subTunes[subEntry]->author = g_strdup(&tmpLine[9]);
			} else if (strncmp(tmpLine, " ARTIST:", 8) == 0) {
				XS_STILDB_MULTI;
				isMulti = TRUE;
				xs_pstrcat(&(tmnode->subTunes[subEntry]->info), &tmpLine[1]);
			} else if (strncmp(tmpLine, "COMMENT:", 8) == 0) {
				XS_STILDB_MULTI;
				isMulti = TRUE;
				xs_pstrcat(&(tmnode->subTunes[subEntry]->info), tmpLine);
			} else {
				if (isMulti) {
					xs_pstrcat(&(tmnode->subTunes[subEntry]->info), " ");
					xs_pstrcat(&(tmnode->subTunes[subEntry]->info), &tmpLine[linePos]);
				} else {
					XS_STILDB_ERR(lineNum, tmpLine,
					"Entry continuation found when isMulti == FALSE.\n");
				}
			}
			break;
		}
		
		XS_CS_FREE(tmpLine);

	} /* while */

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

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

	return 0;
}


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


/* (Re)create index
 */
gint xs_stildb_index(xs_stildb_t *db)
{
	stil_node_t *curr;
	size_t i;

	/* Free old index */
	if (db->pindex) {
		g_free(db->pindex);
		db->pindex = NULL;
	}

	/* Get size of db */
	curr = db->nodes;
	db->n = 0;
	while (curr) {
		db->n++;
		curr = curr->next;
	}

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

		/* Get node-pointers to table */
		i = 0;
		curr = db->nodes;
		while (curr && (i < db->n)) {
			db->pindex[i++] = curr;
			curr = curr->next;
		}

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

	return 0;
}


/* Free a given STIL database
 */
void xs_stildb_free(xs_stildb_t *db)
{
	stil_node_t *curr, *next;

	if (!db)
		return;

	/* Free the memory allocated for nodes */
	curr = db->nodes;
	while (curr) {
		next = curr->next;
		xs_stildb_node_free(curr);
		curr = next;
	}

	db->nodes = NULL;

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

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


/* Get STIL information node from database
 */
stil_node_t *xs_stildb_get_node(xs_stildb_t *db, gchar *filename)
{
	stil_node_t keyItem, *key, **item;

	/* Check the database pointers */
	if (!db || !db->nodes || !db->pindex)
		return NULL;

	/* Look-up index using binary search */
	keyItem.filename = filename;
	key = &keyItem;
	item = bsearch(&key, db->pindex, db->n, sizeof(stil_node_t *), xs_stildb_cmp);
	if (item)
		return *item;
	else
		return NULL;
}