Mercurial > hg > xmms-sid
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; +}