view src/xs_stil.c @ 40:1788f4ce6a44

Numerous changes towards 0.8
author Matti Hamalainen <ccr@tnsp.org>
date Thu, 19 Jun 2003 22:38:01 +0000
parents 183e7cbc1036
children 0d1df20745dd
line wrap: on
line source

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

   STIL-database parsing functions
   
   Mostly written by Matti "ccr" Hamalainen <ccr@tnsp.org>,
   some parts written by Willem Monsuwe <willem@stack.nl>

   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 "xmms-sid.h"
#include <glib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>


/* Variables and constants */
#define XMMS_SID_MAX_BUFSIZE	2048
struct T_sid_stil_info	xs_stil_info;

/*
 * Utility routines
 */
int xs_strcalloc(gchar **result, gchar *str)
{
 if ((result == NULL) || (str == NULL)) return -1;
 
 if (*result != NULL) g_free(*result);
 
 *result = (gchar *) g_malloc(strlen(str)+1);
 
 if (*result == NULL) return -2;
 
 strcpy(*result, str);

 return 0;
}


int xs_strcat(gchar **result, gchar *str)
{
 if ((result == NULL) || (str == NULL)) return -1;

 *result = (gchar *) g_realloc(*result, strlen(*result) + strlen(str) + 1);
 
 if (*result == NULL) return -2;

 strcat(*result, str);

 return 0;
}


/*
 *	Make lowercase, strip evt. extension
 */
static gchar * uncase_strip_fn(gchar *str)
{
	gchar *res;
	gint i, l;
	
	l = str ? strlen(str) : 0;

	res = strrchr(str, '/');

	if (res) res = strrchr(res, '.');

	if (res) l = (res - str);

	res = g_new(gchar, l + 1);

	for (i = 0; i < l; i++) {
		res[i] = tolower(str[i]);
		}

	res[i] = '\0';
	
	return res;
}


static gchar * xs_get_hvscname(gchar *fname)
{
	gchar *p, *q, *r;
	
	p = xs_cfg.stilpath;
	
	q = r = fname;   

	while (*p == *q) {
		if (*q == '/') r = q + 1;
		p++; q++;
	}

	return r;
}


/*
 * Get line (string) from given file to given buffer.
 * Takes care of winDOS CR/LF and *NIX LF formats.
 */
void stil_get_line(gchar *buf, gint bufsize, FILE *f)
{
	gint i;
	
	/* Get the string */
	fgets(buf, bufsize-1, f);

	/* The file may be in DOS CR-LF format,
	   so check for the line endings and
	   remove the \n and \r accordingly
	*/
	i = strlen(buf);
	if (i > 0) {
	if (buf[i-2] == '\r')
		buf[i-2] = '\0';
		else
		buf[i-1] = '\0';
	}
}


/*
	xs_token_skipsp(buf, j);
	token2 = xs_token_getcopy(buf, j, ')');
*/
gint stil_token_skipsp(gchar *buf, gint i)
{
 gint len = strlen(buf);
 
 while ((i < len) && ((buf[i] == 32) || (buf[i] == '\t'))) i++;

 return i;
}


gchar * stil_token_get(gchar *buf, gint i, gchar c)
{
 gint j, len = strlen(buf);
 gchar *res;
  
 /* Find out the end place */
 j = i;
 while ((buf[j] != c) && (j < len)) j++;
 
 /* Malloc some memory */
 len = (j - i);
 res = (gchar *) g_malloc(len+1);
 if (res == NULL) return NULL;
 
 /* Return the token */
 strncpy(res, &buf[i], len);
 res[len] = '\0';
 
 return res;
}


/*
 * Clear the informations
 */
void xs_stil_clearone(T_sid_stil_subtune *tune)
{
 xs_strcalloc(&tune->title,   "\0");
 xs_strcalloc(&tune->name,    "\0");
 xs_strcalloc(&tune->artist,  "\0");
 xs_strcalloc(&tune->comment, "\0");
}

void xs_stil_clear(void)
{
 int i;

 for (i = 0; i < XMMS_SID_STIL_MAXENTRY; i++)
	xs_stil_clearone(&xs_stil_info.subtune[i]);
}


/*
 * Simple string-list handling functions
 */
typedef struct {
	gint	nitems;
	gchar * * items;
} T_stringlist;


int sl_insert(T_stringlist *list, gchar *str)
{
 gchar *res;
 
 /* Check the list pointer */
 if (list == NULL) return -1;
 if (str == NULL) return -2;

 /* Increase the space in pointer list */
 list->nitems++;

 list->items = (gchar * *) g_realloc(list->items, (sizeof(gchar **) * list->nitems));
 if (list->items == NULL) return -3;
 
 /* Allocate space for the string */
 res = (gchar *) g_malloc(strlen(str) + 1);
 if (res == NULL) return -4;
 
 /* Put the data in */
 strcpy(res, str);
 list->items[(list->nitems - 1)] = res;

 /* Return number of items */
 return (list->nitems);
}


gchar * sl_getitem(T_stringlist *list, gint n)
{
 /* Check the list pointer */
 if (list == NULL) return NULL;
 if (list->items == NULL) return NULL;

 /* Check the argument */
 if ((n >= 0) && (n < list->nitems)) return (list->items[n]);
 
 return NULL;
}


int sl_clear(T_stringlist *list)
{
 /* Check the list pointer */
 if (list == NULL) return -1;

 /* Clear the variables */
 list->nitems = 0;
 list->items = NULL; 

 return 0;
}


int sl_free(T_stringlist *list)
{
 gint i;

 /* Check the list pointer */
 if (list == NULL) return -1;

 /* Check the items */
 if (list->items != NULL)
 {
  /* Free all strings in list, if any */
  for (i = 0; i < list->nitems; i++)
  	{
  	if (list->items[i] != NULL)
  		free(list->items[i]);
  	}
  	
  /* Free the list itself */
  free(list->items);
 }

 /* Clear the data */
 list->nitems = 0;
 list->items = NULL;
 
 return 0;
}


/*
 * "Submit" all gathered "lists" to given tunedef
 */
void xs_stil_submit(T_sid_stil_subtune *tune, T_stringlist *iartist,
		   T_stringlist *icomment, T_stringlist *iname, T_stringlist *ititle)
{
 gchar *tmpstr;
 gint i, ok;

 /* Clear the data */
 xs_stil_clearone(tune);

 /* "Submit" lists to tunedata */
 if ((iartist->nitems > 1) ||
     (icomment->nitems > 1) ||
     (iname->nitems > 1) ||
     (ititle->nitems > 1))
	{
	/* Multiple items per category */
	i = 0;
	ok = 1;
	while (ok) {
		/* Clear the flag */
		ok = 0;

		/* Get items from lists */		
		tmpstr = sl_getitem(iartist, i);
		if (tmpstr != NULL) {
			xs_strcat(&tune->comment, "\nArtist: ");
			xs_strcat(&tune->comment, tmpstr);
			ok = 1;
			}
		
		tmpstr = sl_getitem(icomment, i);
		if (tmpstr != NULL) {
			xs_strcat(&tune->comment, "\nComment: ");
			xs_strcat(&tune->comment, tmpstr);
			ok = 1;
			}
		
		tmpstr = sl_getitem(iname, i);
		if (tmpstr != NULL) {
			xs_strcat(&tune->comment, "\nName: ");
			xs_strcat(&tune->comment, tmpstr);
			ok = 1;
			}
		
		tmpstr = sl_getitem(ititle, i);
		if (tmpstr != NULL) {
			xs_strcat(&tune->comment, "\nTitle: ");
			xs_strcat(&tune->comment, tmpstr);
			ok = 1;
			}
		
		/* Next one */	
		i++;				
		}
	} else
	{
	/* Only one item or none */ 
	tmpstr = sl_getitem(iartist, 0);
	if (tmpstr != NULL) xs_strcalloc(&tune->artist, tmpstr);

	tmpstr = sl_getitem(icomment, 0);
	if (tmpstr != NULL) xs_strcalloc(&tune->comment, tmpstr);

	tmpstr = sl_getitem(iname, 0);
	if (tmpstr != NULL) xs_strcalloc(&tune->name, tmpstr);

	tmpstr = sl_getitem(ititle, 0);
	if (tmpstr != NULL) xs_strcalloc(&tune->title, tmpstr);
	}

 /* Free the lists */
 sl_free(iartist);
 sl_free(icomment);
 sl_free(iname);
 sl_free(ititle);
}


/*
 * Parse all STIL data for one song (subsongs, etc)
 */
int xs_stil_parse_entry(FILE *stilf, gchar *buf, gint bufsize)
{
	T_stringlist iartist, icomment, iname, ititle;
	gchar *token1, *token2, *tmpbuf;
	gint ntune, found, found2;
	gint i, j;

	XSDEBUG("token '%s':\n", buf);

	/* Clear and initialize variables */
	ntune = 0;

	sl_clear(&iartist);
	sl_clear(&icomment);
	sl_clear(&iname);
	sl_clear(&ititle);

	tmpbuf = NULL;
	
	/* Ok, it was found! Now get and parse the data */
	found = ntune = 0;

	while ((!feof(stilf)) && (found == 0)) {
	/* Get line from file */
	stil_get_line(buf, bufsize, stilf);

nreadln:
  
	/* Check for empty (end of STIL record) */
	if (buf[0] == '\0') found = 1; else
			
	{
	/* Skip whitespaces and get first token */
	j = 0;
	token1 = (gchar *) (buf);

	/* Check for data types and act accordingly */
	if (token1[0] == '(') {
		j = stil_token_skipsp(buf, j+1);
		if (buf[j] == '#') {
			token2 = stil_token_get(buf, j+1, ')');
			i = atoi(token2);

			if ((i >= 1) || (i < XMMS_SID_STIL_MAXENTRY)) {

				xs_stil_submit(&xs_stil_info.subtune[ntune],
					&iartist,
					&icomment,
					&iname,
					&ititle);

				ntune = i;

				XSDEBUG("tune_num: '%d'\n", ntune);
				}
						
			g_free(token2);
			}
		} else

	if (!strncmp(token1, "COMMENT:", 8)) {
		j = stil_token_skipsp(buf, j + 8);
		token1 = (gchar *) (buf + j);

		if (xs_strcalloc(&tmpbuf, token1)) return -4;

		found2 = 0;
		while ((!feof(stilf)) && (found2 == 0)) {

			/* Read next entry line */
			j = 0;
			stil_get_line(buf, bufsize, stilf);

			/* Check if the comment continues? */
			if (strncmp("         ", buf, 9) != 0) {
			found2 = 1;
			} else {

			/* Get the comment line and parse it */
			j = stil_token_skipsp(buf, j + 9);
			token1 = (gchar *) (buf + j);

			/* Cat to the end */
			if (xs_strcat(&tmpbuf, " ") < 0) return -4;
			if (xs_strcat(&tmpbuf, token1) < 0) return -4;

			} /* if..else */
		} /* while */


		/* Insert the result */
		XSDEBUG("comment: '%s'\n", tmpbuf);
		sl_insert(&icomment, tmpbuf);

		if (tmpbuf != NULL) free(tmpbuf);
		tmpbuf = NULL;

		goto nreadln;	/* EVIL GOTO! */
		} else

	if (!strncmp(token1, "  TITLE:", 8)) {
		j = stil_token_skipsp(buf, j + 8);
		token1 = (gchar *) (buf + j);
			
		XSDEBUG("title  : '%s'\n", token1);
		sl_insert(&ititle, token1);
		} else

	if (!strncmp(token1, " ARTIST:", 8)) {
		j = stil_token_skipsp(buf, j + 8);
		token1 = (gchar *) (buf + j);

		XSDEBUG("artist : '%s'\n", token1);
		sl_insert(&iartist, token1);
		} else

	if (!strncmp(token1, "   NAME:", 8)) {
		j = stil_token_skipsp(buf, j + 8);
		token1 = (gchar *) (buf + j);
			
		XSDEBUG("name   : '%s'\n", token1);
		sl_insert(&iname, token1);
		}
	}
			
	} /* while */


	/* Submit the last entry */
	xs_stil_submit(&xs_stil_info.subtune[ntune],
			&iartist,
			&icomment,
			&iname,
			&ititle);

			
	XSDEBUG("end of tunedef.\n");
	return 0;
}


/*
 * Main routine for searching the STIL-database file
 */
int xs_stil_get(gchar *sidfn)
{
	FILE *stilf;
	gchar *e, *a, *buf;
	guint bufsize;
	gint found, i, result;
	struct stat stilst;


	/* Clear the STIL info */
	xs_stil_clear();

	/* Check the given STIL database filename */
	if ((!xs_cfg.stilpath || !xs_cfg.stilpath[0])) return -1;

	/* Check if the STIL database file exists */
	if (stat(xs_cfg.stilpath, &stilst) < 0) return -1;

	/* Try to allocate the temporary buffer */
	bufsize = (XMMS_SID_MAX_BUFSIZE + 1);
	buf = (gchar *) g_malloc(bufsize);
	if (buf == NULL) return -2;


	/* Try to open the STIL database file */
	stilf = fopen(xs_cfg.stilpath, "r");
	if (!stilf) return -3;

	/* -- */
	e = uncase_strip_fn(xs_get_hvscname(sidfn));
	XSDEBUG("sfn = '%s'\n", e);

	result = found = 0;

	while ((!feof(stilf)) && (found == 0)) {

		stil_get_line(buf, bufsize, stilf);
		
		/* Ignore everything else until a filename is found */
		if (buf[0] == '/') {
		
			/* Check against our sidname */
			a = uncase_strip_fn((gchar *) (buf+1));
			i = strcmp(a, e);
			g_free(a);

			/* Parse entry if found */
			if (!i) {
				result = xs_stil_parse_entry(stilf, buf, bufsize);
				found = 1;
				}
				
		} /* if (buf[0]... */
		
	} /* while */
	
	/* Shutdown & close */
	g_free(e);
	g_free(buf);
	
	if (!fclose(stilf)) return -3;

	/* Successful return ?? */
	if ((found) && (result >= 0))
		return 0;
		else
		return 1;
}