1
|
1 /*
|
|
2 xmms-sid - SIDPlay input plugin for X MultiMedia System (XMMS)
|
|
3
|
|
4 STIL-database parsing functions
|
|
5
|
|
6 Mostly written by Matti "ccr" Hamalainen <mhamalai@ratol.fi>,
|
|
7 some parts written by Willem Monsuwe <willem@stack.nl>
|
|
8
|
|
9 This program is free software; you can redistribute it and/or modify
|
|
10 it under the terms of the GNU General Public License as published by
|
|
11 the Free Software Foundation; either version 2 of the License, or
|
|
12 (at your option) any later version.
|
|
13
|
|
14 This program is distributed in the hope that it will be useful,
|
|
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
17 GNU General Public License for more details.
|
|
18
|
|
19 You should have received a copy of the GNU General Public License
|
|
20 along with this program; if not, write to the Free Software
|
|
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
22 */
|
|
23
|
|
24 #include "xmms-sid.h"
|
|
25 #include <glib.h>
|
|
26 #include <stdio.h>
|
|
27 #include <sys/stat.h>
|
|
28 #include <unistd.h>
|
|
29 #include <stdlib.h>
|
|
30 #include <string.h>
|
|
31 #include <ctype.h>
|
|
32
|
|
33
|
|
34 /* Variables and constants */
|
|
35 #define XMMS_SID_MAX_BUFSIZE 2048
|
|
36 struct T_sid_stil_info xs_stil_info;
|
|
37
|
|
38 /*
|
|
39 * Utility routines
|
|
40 */
|
|
41 int xs_strcalloc(gchar **result, gchar *str)
|
|
42 {
|
|
43 if ((result == NULL) || (str == NULL)) return -1;
|
|
44
|
|
45 if (*result != NULL) g_free(*result);
|
|
46
|
|
47 *result = (gchar *) g_malloc(strlen(str)+1);
|
|
48
|
|
49 if (*result == NULL) return -2;
|
|
50
|
|
51 strcpy(*result, str);
|
|
52
|
|
53 return 0;
|
|
54 }
|
|
55
|
|
56
|
|
57 int xs_strcat(gchar **result, gchar *str)
|
|
58 {
|
|
59 if ((result == NULL) || (str == NULL)) return -1;
|
|
60
|
|
61 *result = (gchar *) g_realloc(*result, strlen(*result) + strlen(str) + 1);
|
|
62
|
|
63 if (*result == NULL) return -2;
|
|
64
|
|
65 strcat(*result, str);
|
|
66
|
|
67 return 0;
|
|
68 }
|
|
69
|
|
70
|
|
71 /*
|
|
72 * Make lowercase, strip evt. extension
|
|
73 */
|
|
74 static gchar * uncase_strip_fn(gchar *str)
|
|
75 {
|
|
76 gchar *res;
|
|
77 gint i, l;
|
|
78
|
|
79 l = str ? strlen(str) : 0;
|
|
80
|
|
81 res = strrchr(str, '/');
|
|
82
|
|
83 if (res) res = strrchr(res, '.');
|
|
84
|
|
85 if (res) l = (res - str);
|
|
86
|
|
87 res = g_new(gchar, l + 1);
|
|
88
|
|
89 for (i = 0; i < l; i++) {
|
|
90 res[i] = tolower(str[i]);
|
|
91 }
|
|
92
|
|
93 res[i] = '\0';
|
|
94
|
|
95 return res;
|
|
96 }
|
|
97
|
|
98
|
|
99 static gchar * xs_get_hvscname(gchar *fname)
|
|
100 {
|
|
101 gchar *p, *q, *r;
|
|
102
|
|
103 p = xs_cfg.stilpath;
|
|
104
|
|
105 q = r = fname;
|
|
106
|
|
107 while (*p == *q) {
|
|
108 if (*q == '/') r = q + 1;
|
|
109 p++; q++;
|
|
110 }
|
|
111
|
|
112 return r;
|
|
113 }
|
|
114
|
|
115
|
|
116 /*
|
|
117 * Get line (string) from given file to given buffer.
|
|
118 * Takes care of winDOS CR/LF and *NIX LF formats.
|
|
119 */
|
|
120 void stil_get_line(gchar *buf, gint bufsize, FILE *f)
|
|
121 {
|
|
122 gint i;
|
|
123
|
|
124 /* Get the string */
|
|
125 fgets(buf, bufsize-1, f);
|
|
126
|
|
127 /* The file may be in DOS CR-LF format,
|
|
128 so check for the line endings and
|
|
129 remove the \n and \r accordingly
|
|
130 */
|
|
131 i = strlen(buf);
|
|
132 if (i > 0) {
|
|
133 if (buf[i-2] == '\r')
|
|
134 buf[i-2] = '\0';
|
|
135 else
|
|
136 buf[i-1] = '\0';
|
|
137 }
|
|
138 }
|
|
139
|
|
140
|
|
141 /*
|
|
142 xs_token_skipsp(buf, j);
|
|
143 token2 = xs_token_getcopy(buf, j, ')');
|
|
144 */
|
|
145 gint stil_token_skipsp(gchar *buf, gint i)
|
|
146 {
|
|
147 gint len = strlen(buf);
|
|
148
|
|
149 while ((i < len) && ((buf[i] == 32) || (buf[i] == '\t'))) i++;
|
|
150
|
|
151 return i;
|
|
152 }
|
|
153
|
|
154
|
|
155 gchar * stil_token_get(gchar *buf, gint i, gchar c)
|
|
156 {
|
|
157 gint j, len = strlen(buf);
|
|
158 gchar *res;
|
|
159
|
|
160 /* Find out the end place */
|
|
161 j = i;
|
|
162 while ((buf[j] != c) && (j < len)) j++;
|
|
163
|
|
164 /* Malloc some memory */
|
|
165 len = (j - i);
|
|
166 res = (gchar *) g_malloc(len+1);
|
|
167 if (res == NULL) return NULL;
|
|
168
|
|
169 /* Return the token */
|
|
170 strncpy(res, &buf[i], len);
|
|
171 res[len] = '\0';
|
|
172
|
|
173 return res;
|
|
174 }
|
|
175
|
|
176
|
|
177 /*
|
|
178 * Clear the informations
|
|
179 */
|
|
180 void xs_stil_clearone(T_sid_stil_subtune *tune)
|
|
181 {
|
|
182 xs_strcalloc(&tune->title, "\0");
|
|
183 xs_strcalloc(&tune->name, "\0");
|
|
184 xs_strcalloc(&tune->artist, "\0");
|
|
185 xs_strcalloc(&tune->comment, "\0");
|
|
186 }
|
|
187
|
|
188 void xs_stil_clear(void)
|
|
189 {
|
|
190 int i;
|
|
191
|
|
192 for (i = 0; i < XMMS_SID_STIL_MAXENTRY; i++)
|
|
193 xs_stil_clearone(&xs_stil_info.subtune[i]);
|
|
194 }
|
|
195
|
|
196
|
|
197 /*
|
|
198 * Simple string-list handling functions
|
|
199 */
|
|
200 typedef struct {
|
|
201 gint nitems;
|
|
202 gchar * * items;
|
|
203 } T_stringlist;
|
|
204
|
|
205
|
|
206 int sl_insert(T_stringlist *list, gchar *str)
|
|
207 {
|
|
208 gchar *res;
|
|
209
|
|
210 /* Check the list pointer */
|
|
211 if (list == NULL) return -1;
|
|
212 if (str == NULL) return -2;
|
|
213
|
|
214 /* Increase the space in pointer list */
|
|
215 list->nitems++;
|
|
216
|
|
217 list->items = (gchar * *) g_realloc(list->items, (sizeof(gchar **) * list->nitems));
|
|
218 if (list->items == NULL) return -3;
|
|
219
|
|
220 /* Allocate space for the string */
|
|
221 res = (gchar *) g_malloc(strlen(str) + 1);
|
|
222 if (res == NULL) return -4;
|
|
223
|
|
224 /* Put the data in */
|
|
225 strcpy(res, str);
|
|
226 list->items[(list->nitems - 1)] = res;
|
|
227
|
|
228 /* Return number of items */
|
|
229 return (list->nitems);
|
|
230 }
|
|
231
|
|
232
|
|
233 gchar * sl_getitem(T_stringlist *list, gint n)
|
|
234 {
|
|
235 /* Check the list pointer */
|
|
236 if (list == NULL) return NULL;
|
|
237 if (list->items == NULL) return NULL;
|
|
238
|
|
239 /* Check the argument */
|
|
240 if ((n >= 0) && (n < list->nitems)) return (list->items[n]);
|
|
241
|
|
242 return NULL;
|
|
243 }
|
|
244
|
|
245
|
|
246 int sl_clear(T_stringlist *list)
|
|
247 {
|
|
248 /* Check the list pointer */
|
|
249 if (list == NULL) return -1;
|
|
250
|
|
251 /* Clear the variables */
|
|
252 list->nitems = 0;
|
|
253 list->items = NULL;
|
|
254
|
|
255 return 0;
|
|
256 }
|
|
257
|
|
258
|
|
259 int sl_free(T_stringlist *list)
|
|
260 {
|
|
261 gint i;
|
|
262
|
|
263 /* Check the list pointer */
|
|
264 if (list == NULL) return -1;
|
|
265
|
|
266 /* Check the items */
|
|
267 if (list->items != NULL)
|
|
268 {
|
|
269 /* Free all strings in list, if any */
|
|
270 for (i = 0; i < list->nitems; i++)
|
|
271 {
|
|
272 if (list->items[i] != NULL)
|
|
273 free(list->items[i]);
|
|
274 }
|
|
275
|
|
276 /* Free the list itself */
|
|
277 free(list->items);
|
|
278 }
|
|
279
|
|
280 /* Clear the data */
|
|
281 list->nitems = 0;
|
|
282 list->items = NULL;
|
|
283
|
|
284 return 0;
|
|
285 }
|
|
286
|
|
287
|
|
288 /*
|
|
289 * "Submit" all gathered "lists" to given tunedef
|
|
290 */
|
|
291 void xs_stil_submit(T_sid_stil_subtune *tune, T_stringlist *iartist,
|
|
292 T_stringlist *icomment, T_stringlist *iname, T_stringlist *ititle)
|
|
293 {
|
|
294 gchar *tmpstr;
|
|
295 gint i, ok;
|
|
296
|
|
297 /* Clear the data */
|
|
298 xs_stil_clearone(tune);
|
|
299
|
|
300 /* "Submit" lists to tunedata */
|
|
301 if ((iartist->nitems > 1) ||
|
|
302 (icomment->nitems > 1) ||
|
|
303 (iname->nitems > 1) ||
|
|
304 (ititle->nitems > 1))
|
|
305 {
|
|
306 /* Multiple items per category */
|
|
307 i = 0;
|
|
308 ok = 1;
|
|
309 while (ok) {
|
|
310 /* Clear the flag */
|
|
311 ok = 0;
|
|
312
|
|
313 /* Get items from lists */
|
|
314 tmpstr = sl_getitem(iartist, i);
|
|
315 if (tmpstr != NULL) {
|
|
316 xs_strcat(&tune->comment, "\nArtist: ");
|
|
317 xs_strcat(&tune->comment, tmpstr);
|
|
318 ok = 1;
|
|
319 }
|
|
320
|
|
321 tmpstr = sl_getitem(icomment, i);
|
|
322 if (tmpstr != NULL) {
|
|
323 xs_strcat(&tune->comment, "\nComment: ");
|
|
324 xs_strcat(&tune->comment, tmpstr);
|
|
325 ok = 1;
|
|
326 }
|
|
327
|
|
328 tmpstr = sl_getitem(iname, i);
|
|
329 if (tmpstr != NULL) {
|
|
330 xs_strcat(&tune->comment, "\nName: ");
|
|
331 xs_strcat(&tune->comment, tmpstr);
|
|
332 ok = 1;
|
|
333 }
|
|
334
|
|
335 tmpstr = sl_getitem(ititle, i);
|
|
336 if (tmpstr != NULL) {
|
|
337 xs_strcat(&tune->comment, "\nTitle: ");
|
|
338 xs_strcat(&tune->comment, tmpstr);
|
|
339 ok = 1;
|
|
340 }
|
|
341
|
|
342 /* Next one */
|
|
343 i++;
|
|
344 }
|
|
345 } else
|
|
346 {
|
|
347 /* Only one item or none */
|
|
348 tmpstr = sl_getitem(iartist, 0);
|
|
349 if (tmpstr != NULL) xs_strcalloc(&tune->artist, tmpstr);
|
|
350
|
|
351 tmpstr = sl_getitem(icomment, 0);
|
|
352 if (tmpstr != NULL) xs_strcalloc(&tune->comment, tmpstr);
|
|
353
|
|
354 tmpstr = sl_getitem(iname, 0);
|
|
355 if (tmpstr != NULL) xs_strcalloc(&tune->name, tmpstr);
|
|
356
|
|
357 tmpstr = sl_getitem(ititle, 0);
|
|
358 if (tmpstr != NULL) xs_strcalloc(&tune->title, tmpstr);
|
|
359 }
|
|
360
|
|
361 /* Free the lists */
|
|
362 sl_free(iartist);
|
|
363 sl_free(icomment);
|
|
364 sl_free(iname);
|
|
365 sl_free(ititle);
|
|
366 }
|
|
367
|
|
368
|
|
369 /*
|
|
370 * Parse all STIL data for one song (subsongs, etc)
|
|
371 */
|
|
372 int xs_stil_parse_entry(FILE *stilf, gchar *buf, gint bufsize)
|
|
373 {
|
|
374 T_stringlist iartist, icomment, iname, ititle;
|
|
375 gchar *token1, *token2, *tmpbuf;
|
|
376 gint ntune, found, found2;
|
|
377 gint i, j;
|
|
378
|
|
379 XSDEBUG("token '%s':\n", buf);
|
|
380
|
|
381 /* Clear and initialize variables */
|
|
382 ntune = 0;
|
|
383
|
|
384 sl_clear(&iartist);
|
|
385 sl_clear(&icomment);
|
|
386 sl_clear(&iname);
|
|
387 sl_clear(&ititle);
|
|
388
|
|
389 tmpbuf = NULL;
|
|
390
|
|
391 /* Ok, it was found! Now get and parse the data */
|
|
392 found = ntune = 0;
|
|
393
|
|
394 while ((!feof(stilf)) && (found == 0)) {
|
|
395 /* Get line from file */
|
|
396 stil_get_line(buf, bufsize, stilf);
|
|
397
|
|
398 nreadln:
|
|
399
|
|
400 /* Check for empty (end of STIL record) */
|
|
401 if (buf[0] == '\0') found = 1; else
|
|
402
|
|
403 {
|
|
404 /* Skip whitespaces and get first token */
|
|
405 j = 0;
|
|
406 token1 = (gchar *) (buf);
|
|
407
|
|
408 /* Check for data types and act accordingly */
|
|
409 if (token1[0] == '(') {
|
|
410 j = stil_token_skipsp(buf, j+1);
|
|
411 if (buf[j] == '#') {
|
|
412 token2 = stil_token_get(buf, j+1, ')');
|
|
413 i = atoi(token2);
|
|
414
|
|
415 if ((i >= 1) || (i < XMMS_SID_STIL_MAXENTRY)) {
|
|
416
|
|
417 xs_stil_submit(&xs_stil_info.subtune[ntune],
|
|
418 &iartist,
|
|
419 &icomment,
|
|
420 &iname,
|
|
421 &ititle);
|
|
422
|
|
423 ntune = i;
|
|
424
|
|
425 XSDEBUG("tune_num: '%d'\n", ntune);
|
|
426 }
|
|
427
|
|
428 g_free(token2);
|
|
429 }
|
|
430 } else
|
|
431
|
|
432 if (!strncmp(token1, "COMMENT:", 8)) {
|
|
433 j = stil_token_skipsp(buf, j + 8);
|
|
434 token1 = (gchar *) (buf + j);
|
|
435
|
|
436 if (xs_strcalloc(&tmpbuf, token1)) return -4;
|
|
437
|
|
438 found2 = 0;
|
|
439 while ((!feof(stilf)) && (found2 == 0)) {
|
|
440
|
|
441 /* Read next entry line */
|
|
442 j = 0;
|
|
443 stil_get_line(buf, bufsize, stilf);
|
|
444
|
|
445 /* Check if the comment continues? */
|
|
446 if (strncmp(" ", buf, 9) != 0) {
|
|
447 found2 = 1;
|
|
448 } else {
|
|
449
|
|
450 /* Get the comment line and parse it */
|
|
451 j = stil_token_skipsp(buf, j + 9);
|
|
452 token1 = (gchar *) (buf + j);
|
|
453
|
|
454 /* Cat to the end */
|
|
455 if (xs_strcat(&tmpbuf, " ") < 0) return -4;
|
|
456 if (xs_strcat(&tmpbuf, token1) < 0) return -4;
|
|
457
|
|
458 } /* if..else */
|
|
459 } /* while */
|
|
460
|
|
461
|
|
462 /* Insert the result */
|
|
463 XSDEBUG("comment: '%s'\n", tmpbuf);
|
|
464 sl_insert(&icomment, tmpbuf);
|
|
465
|
|
466 if (tmpbuf != NULL) free(tmpbuf);
|
|
467 tmpbuf = NULL;
|
|
468
|
|
469 goto nreadln; /* EVIL GOTO! */
|
|
470 } else
|
|
471
|
|
472 if (!strncmp(token1, " TITLE:", 8)) {
|
|
473 j = stil_token_skipsp(buf, j + 8);
|
|
474 token1 = (gchar *) (buf + j);
|
|
475
|
|
476 XSDEBUG("title : '%s'\n", token1);
|
|
477 sl_insert(&ititle, token1);
|
|
478 } else
|
|
479
|
|
480 if (!strncmp(token1, " ARTIST:", 8)) {
|
|
481 j = stil_token_skipsp(buf, j + 8);
|
|
482 token1 = (gchar *) (buf + j);
|
|
483
|
|
484 XSDEBUG("artist : '%s'\n", token1);
|
|
485 sl_insert(&iartist, token1);
|
|
486 } else
|
|
487
|
|
488 if (!strncmp(token1, " NAME:", 8)) {
|
|
489 j = stil_token_skipsp(buf, j + 8);
|
|
490 token1 = (gchar *) (buf + j);
|
|
491
|
|
492 XSDEBUG("name : '%s'\n", token1);
|
|
493 sl_insert(&iname, token1);
|
|
494 }
|
|
495 }
|
|
496
|
|
497 } /* while */
|
|
498
|
|
499
|
|
500 /* Submit the last entry */
|
|
501 xs_stil_submit(&xs_stil_info.subtune[ntune],
|
|
502 &iartist,
|
|
503 &icomment,
|
|
504 &iname,
|
|
505 &ititle);
|
|
506
|
|
507
|
|
508 XSDEBUG("end of tunedef.\n");
|
|
509 return 0;
|
|
510 }
|
|
511
|
|
512
|
|
513 /*
|
|
514 * Main routine for searching the STIL-database file
|
|
515 */
|
|
516 int xs_stil_get(gchar *sidfn)
|
|
517 {
|
|
518 FILE *stilf;
|
|
519 gchar *e, *a, *buf;
|
|
520 guint bufsize;
|
|
521 gint found, i, result;
|
|
522 struct stat stilst;
|
|
523
|
|
524
|
|
525 /* Clear the STIL info */
|
|
526 xs_stil_clear();
|
|
527
|
|
528 /* Check the given STIL database filename */
|
|
529 if ((!xs_cfg.stilpath || !xs_cfg.stilpath[0])) return -1;
|
|
530
|
|
531 /* Check if the STIL database file exists */
|
|
532 if (stat(xs_cfg.stilpath, &stilst) < 0) return -1;
|
|
533
|
|
534 /* Try to allocate the temporary buffer */
|
|
535 bufsize = (XMMS_SID_MAX_BUFSIZE + 1);
|
|
536 buf = (gchar *) g_malloc(bufsize);
|
|
537 if (buf == NULL) return -2;
|
|
538
|
|
539
|
|
540 /* Try to open the STIL database file */
|
|
541 stilf = fopen(xs_cfg.stilpath, "r");
|
|
542 if (!stilf) return -3;
|
|
543
|
|
544 /* -- */
|
|
545 e = uncase_strip_fn(xs_get_hvscname(sidfn));
|
|
546 XSDEBUG("sfn = '%s'\n", e);
|
|
547
|
|
548 result = found = 0;
|
|
549
|
|
550 while ((!feof(stilf)) && (found == 0)) {
|
|
551
|
|
552 stil_get_line(buf, bufsize, stilf);
|
|
553
|
|
554 /* Ignore everything else until a filename is found */
|
|
555 if (buf[0] == '/') {
|
|
556
|
|
557 /* Check against our sidname */
|
|
558 a = uncase_strip_fn((gchar *) (buf+1));
|
|
559 i = strcmp(a, e);
|
|
560 g_free(a);
|
|
561
|
|
562 /* Parse entry if found */
|
|
563 if (!i) {
|
|
564 result = xs_stil_parse_entry(stilf, buf, bufsize);
|
|
565 found = 1;
|
|
566 }
|
|
567
|
|
568 } /* if (buf[0]... */
|
|
569
|
|
570 } /* while */
|
|
571
|
|
572 /* Shutdown & close */
|
|
573 g_free(e);
|
|
574 g_free(buf);
|
|
575
|
|
576 if (!fclose(stilf)) return -3;
|
|
577
|
|
578 /* Successful return ?? */
|
|
579 if ((found) && (result >= 0))
|
|
580 return 0;
|
|
581 else
|
|
582 return 1;
|
|
583 }
|