diff src/xs_stil.c @ 1:183e7cbc1036

Initial revision
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 03 Jun 2003 10:23:04 +0000
parents
children 1788f4ce6a44
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/xs_stil.c	Tue Jun 03 10:23:04 2003 +0000
@@ -0,0 +1,583 @@
+/*  
+   xmms-sid - SIDPlay input plugin for X MultiMedia System (XMMS)
+
+   STIL-database parsing functions
+   
+   Mostly written by Matti "ccr" Hamalainen <mhamalai@ratol.fi>,
+   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;
+}