Mercurial > audlegacy
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 |