comparison src/audacious/util.c @ 2529:299be5908480 trunk

[svn] - made new INI file parser - removed old-style read_ini_string() function - made skin routines use new INI parser
author mf0102
date Thu, 15 Feb 2007 15:17:36 -0800
parents 7934ac463591
children 22da0618297e
comparison
equal deleted inserted replaced
2528:60f2787cc7fd 2529:299be5908480
363 rmdir(path); 363 rmdir(path);
364 } 364 }
365 365
366 #endif /* ifdef HAVE_FTS */ 366 #endif /* ifdef HAVE_FTS */
367 367
368 gchar * 368 static void
369 read_ini_string(const gchar * filename, const gchar * section, 369 strip_string(GString *string)
370 const gchar * key) 370 {
371 { 371 while (string->len > 0 && string->str[0] == ' ')
372 static gchar *buffer = NULL; 372 g_string_erase(string, 0, 1);
373 static gchar *open_buffer = NULL; 373
374 gchar *ret_buffer = NULL; 374 while (string->len > 0 && string->str[string->len - 1] == ' ')
375 gint found_section = 0, len = 0; 375 g_string_erase(string, string->len - 1, 1);
376 static gsize filesize = 0; 376 }
377
378 static void
379 strip_lower_string(GString *string)
380 {
381 strip_string(string);
382
383 gchar *lower = g_ascii_strdown(string->str, -1);
384 g_free(string->str);
385 string->str = lower;
386 }
387
388 INIFile *
389 open_ini_file(const gchar *filename)
390 {
391 GHashTable *ini_file = g_hash_table_new(NULL, NULL);
392 GHashTable *section = g_hash_table_new(NULL, NULL);
393 GString *section_name, *key_name, *value;
394 gpointer section_hash, key_hash;
395 gchar *buffer = NULL;
377 gsize off = 0; 396 gsize off = 0;
378 gchar *outbuf; 397 gsize filesize = 0;
398
379 unsigned char x[] = { 0xff, 0xfe, 0x00 }; 399 unsigned char x[] = { 0xff, 0xfe, 0x00 };
380 guint counter; 400
381 401
382 if (!filename) 402 g_return_val_if_fail(filename, NULL);
403
404 section_name = g_string_new("");
405 key_name = g_string_new(NULL);
406 value = g_string_new(NULL);
407
408 /* make a nameless section which should store all entries that are not
409 * embedded in a section */
410 section_hash = GINT_TO_POINTER(g_string_hash(section_name));
411 g_hash_table_insert(ini_file, section_hash, section);
412
413 vfs_file_get_contents(filename, &buffer, &filesize);
414 if (buffer == NULL)
383 return NULL; 415 return NULL;
384 416
385 /*
386 * We optimise for the condition that we may be reading from the
387 * same ini-file multiple times. This is fairly common; it happens
388 * on .pls playlist loads. To do otherwise would take entirely too
389 * long, as fstat() can be very slow when done 21,000 times too many.
390 *
391 * Therefore, we optimise by keeping the last ini file in memory.
392 * Yes, this is a memory leak, but it is not too bad, hopefully.
393 * - nenolod
394 */
395 if (open_buffer == NULL || strcasecmp(filename, open_buffer))
396 {
397 if (buffer != NULL)
398 {
399 g_free(buffer);
400 buffer = NULL;
401 }
402
403 if (open_buffer != NULL)
404 {
405 g_free(open_buffer);
406 open_buffer = NULL;
407 }
408
409 vfs_file_get_contents(filename, &buffer, &filesize);
410
411 if (buffer == NULL)
412 return NULL;
413
414 open_buffer = g_strdup(filename);
415 }
416 417
417 /* 418 /*
418 * Convert UTF-16 into something useful. Original implementation 419 * Convert UTF-16 into something useful. Original implementation
419 * by incomp@#audacious. Cleanups \nenolod 420 * by incomp@#audacious. Cleanups \nenolod
421 * FIXME: can't we use a GLib function for that? -- 01mf02
420 */ 422 */
421 if (!memcmp(&buffer[0],&x,2)) { 423 if (!memcmp(&buffer[0],&x,2))
422 outbuf = g_malloc (filesize); /* it's safe to waste memory. */ 424 {
425 gchar *outbuf = g_malloc (filesize); /* it's safe to waste memory. */
426 guint counter;
423 427
424 for (counter = 2; counter < filesize; counter += 2) 428 for (counter = 2; counter < filesize; counter += 2)
429 {
425 if (!memcmp(&buffer[counter+1], &x[2], 1)) 430 if (!memcmp(&buffer[counter+1], &x[2], 1))
426 outbuf[(counter-2)/2] = buffer[counter]; 431 outbuf[(counter-2)/2] = buffer[counter];
427 else 432 else
428 return NULL; 433 return NULL;
434 }
429 435
430 outbuf[(counter-2)/2] = '\0'; 436 outbuf[(counter-2)/2] = '\0';
431 437
432 if ((filesize - 2) / 2 == (counter - 2) / 2) { 438 if ((filesize - 2) / 2 == (counter - 2) / 2)
439 {
433 g_free(buffer); 440 g_free(buffer);
434 buffer = outbuf; 441 buffer = outbuf;
435 } else { 442 }
443 else
444 {
436 g_free(outbuf); 445 g_free(outbuf);
437 return NULL; /* XXX wrong encoding */ 446 return NULL; /* XXX wrong encoding */
438 } 447 }
439 } 448 }
440 449
441 while (!ret_buffer && off < filesize) { 450 while (off < filesize)
442 while (off < filesize && 451 {
443 (buffer[off] == '\r' || buffer[off] == '\n' || 452 /* ignore the following characters */
444 buffer[off] == ' ' || buffer[off] == '\t')) 453 if (buffer[off] == '\r' || buffer[off] == '\n' ||
454 buffer[off] == ' ' || buffer[off] == '\t')
455 {
456 if (buffer[off] == '\n')
457 {
458 g_string_free(key_name, TRUE);
459 g_string_free(value, FALSE);
460 key_name = g_string_new(NULL);
461 value = g_string_new(NULL);
462 }
463
445 off++; 464 off++;
446 if (off >= filesize) 465 continue;
447 break; 466 }
448 if (buffer[off] == '[') { 467
449 gint slen = strlen(section); 468 /* if we encounter a possible section statement */
469 if (buffer[off] == '[')
470 {
471 g_string_free(section_name, TRUE);
472 section_name = g_string_new(NULL);
450 off++; 473 off++;
451 found_section = 0; 474
452 if (off + slen + 1 < filesize && 475 if (off >= filesize)
453 !strncasecmp(section, &buffer[off], slen)) { 476 goto return_sequence;
454 off += slen; 477
455 if (buffer[off] == ']') { 478 while (buffer[off] != ']')
456 off++; 479 {
457 found_section = 1; 480 /* if the section statement has not been closed before a
481 * linebreak */
482 if (buffer[off] == '\n')
483 break;
484
485 g_string_append_c(section_name, buffer[off]);
486 off++;
487 if (off >= filesize)
488 goto return_sequence;
489 }
490 if (buffer[off] == '\n')
491 continue;
492 if (buffer[off] == ']')
493 {
494 off++;
495 if (off >= filesize)
496 goto return_sequence;
497
498 strip_lower_string(section_name);
499 section_hash = GINT_TO_POINTER(g_string_hash(section_name));
500
501 /* if this section already exists, we don't make a new one,
502 * but reuse the old one */
503 if (g_hash_table_lookup(ini_file, section_hash) != NULL)
504 section = g_hash_table_lookup(ini_file, section_hash);
505 else
506 {
507 section = g_hash_table_new(NULL, NULL);
508 g_hash_table_insert(ini_file, section_hash, section);
458 } 509 }
510
511 continue;
459 } 512 }
460 } 513 }
461 else if (found_section && off + strlen(key) < filesize && 514
462 !strncasecmp(key, &buffer[off], strlen(key))) { 515 if (buffer[off] == '=')
463 off += strlen(key); 516 {
464 while (off < filesize && 517 off++;
465 (buffer[off] == ' ' || buffer[off] == '\t')) 518 if (off >= filesize)
519 goto return_sequence;
520
521 while (buffer[off] != '\n')
522 {
523 g_string_append_c(value, buffer[off]);
466 off++; 524 off++;
467 if (off >= filesize)
468 break;
469 if (buffer[off] == '=') {
470 off++;
471 while (off < filesize &&
472 (buffer[off] == ' ' || buffer[off] == '\t'))
473 off++;
474 if (off >= filesize) 525 if (off >= filesize)
475 break; 526 break;
476 len = 0;
477 while (off + len < filesize &&
478 buffer[off + len] != '\r' &&
479 buffer[off + len] != '\n' && buffer[off + len] != ';')
480 len++;
481 ret_buffer = g_strndup(&buffer[off], len);
482 off += len;
483 } 527 }
484 } 528
485 while (off < filesize && buffer[off] != '\r' && buffer[off] != '\n') 529 strip_lower_string(key_name);
530 key_hash = GINT_TO_POINTER(g_string_hash(key_name));
531 strip_string(value);
532
533 if (key_name->len > 0 && value->len > 0)
534 g_hash_table_insert(section, key_hash, value->str);
535 }
536 else
537 {
538 g_string_append_c(key_name, buffer[off]);
486 off++; 539 off++;
487 } 540 if (off >= filesize)
488 541 goto return_sequence;
489 return ret_buffer; 542 }
543 }
544
545 return_sequence:
546 g_string_free(section_name, TRUE);
547 g_string_free(key_name, TRUE);
548 g_string_free(value, TRUE);
549 g_free(buffer);
550 return ini_file;
551 }
552
553 void
554 close_ini_file(INIFile *inifile)
555 {
556 g_return_if_fail(inifile);
557
558 /* we don't have to destroy anything in the hash table manually, as the
559 * keys are represented as integers and the string values may be used in
560 * functions which have read the strings from the hash table
561 */
562 g_hash_table_destroy(inifile);
563 }
564
565 gchar *
566 read_ini_string(INIFile *inifile, const gchar *section, const gchar *key)
567 {
568 g_return_val_if_fail(inifile, NULL);
569
570 GString *section_string = g_string_new(section);
571 GString *key_string = g_string_new(key);
572 gchar *value = NULL;
573
574 strip_lower_string(section_string);
575 strip_lower_string(key_string);
576 gpointer section_hash = GINT_TO_POINTER(g_string_hash(section_string));
577 gpointer key_hash = GINT_TO_POINTER(g_string_hash(key_string));
578 g_string_free(section_string, FALSE);
579 g_string_free(key_string, FALSE);
580
581 GHashTable *section_table = g_hash_table_lookup(inifile, section_hash);
582 g_return_val_if_fail(section_table, NULL);
583
584 value = g_hash_table_lookup(section_table, GINT_TO_POINTER(key_hash));
585 return value;
490 } 586 }
491 587
492 GArray * 588 GArray *
493 read_ini_array(const gchar * filename, const gchar * section, 589 read_ini_array(INIFile *inifile, const gchar *section, const gchar *key)
494 const gchar * key)
495 { 590 {
496 gchar *temp; 591 gchar *temp;
497 GArray *a; 592 GArray *a;
498 593
499 if ((temp = read_ini_string(filename, section, key)) == NULL) { 594 g_return_val_if_fail((temp = read_ini_string(inifile, section, key)), NULL);
500 g_free(temp); 595
501 return NULL;
502 }
503 a = string_to_garray(temp); 596 a = string_to_garray(temp);
504 g_free(temp); 597 g_free(temp);
505 return a; 598 return a;
506 } 599 }
507 600