Audacious $Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
00001 /* 00002 * Audacious 00003 * Copyright (c) 2006-2007 Audacious team 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License as published by 00007 * the Free Software Foundation; under version 3 of the License. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program. If not, see <http://www.gnu.org/licenses>. 00016 * 00017 * The Audacious team does not consider modular code linking to 00018 * Audacious or using our public API to be a derived work. 00019 */ 00025 #include <glib.h> 00026 #include <mowgli.h> 00027 00028 #include "tuple.h" 00029 #include "audstrings.h" 00030 #include "stringpool.h" 00031 00034 const TupleBasicType tuple_fields[FIELD_LAST] = { 00035 { "artist", TUPLE_STRING }, 00036 { "title", TUPLE_STRING }, 00037 { "album", TUPLE_STRING }, 00038 { "comment", TUPLE_STRING }, 00039 { "genre", TUPLE_STRING }, 00040 00041 { "track", TUPLE_STRING }, 00042 { "track-number", TUPLE_INT }, 00043 { "length", TUPLE_INT }, 00044 { "year", TUPLE_INT }, 00045 { "quality", TUPLE_STRING }, 00046 00047 { "codec", TUPLE_STRING }, 00048 { "file-name", TUPLE_STRING }, 00049 { "file-path", TUPLE_STRING }, 00050 { "file-ext", TUPLE_STRING }, 00051 { "song-artist", TUPLE_STRING }, 00052 00053 { "mtime", TUPLE_INT }, 00054 { "formatter", TUPLE_STRING }, 00055 { "performer", TUPLE_STRING }, 00056 { "copyright", TUPLE_STRING }, 00057 { "date", TUPLE_STRING }, 00058 00059 { "subsong-id", TUPLE_INT }, 00060 { "subsong-num", TUPLE_INT }, 00061 { "mime-type", TUPLE_STRING }, 00062 { "bitrate", TUPLE_INT }, 00063 00064 { "segment-start", TUPLE_INT }, 00065 { "segment-end", TUPLE_INT }, 00066 00067 { "gain-album-gain", TUPLE_INT }, 00068 { "gain-album-peak", TUPLE_INT }, 00069 { "gain-track-gain", TUPLE_INT }, 00070 { "gain-track-peak", TUPLE_INT }, 00071 { "gain-gain-unit", TUPLE_INT }, 00072 { "gain-peak-unit", TUPLE_INT }, 00073 00074 { "composer", TUPLE_STRING }, 00075 }; 00076 00077 00079 static mowgli_heap_t *tuple_heap = NULL; 00080 00082 static mowgli_heap_t *tuple_value_heap = NULL; 00083 static mowgli_object_class_t tuple_klass; 00084 00086 static GStaticRWLock tuple_rwlock = G_STATIC_RW_LOCK_INIT; 00087 00089 00093 #define TUPLE_LOCK_WRITE(X) g_static_rw_lock_writer_lock(&tuple_rwlock) 00094 #define TUPLE_UNLOCK_WRITE(X) g_static_rw_lock_writer_unlock(&tuple_rwlock) 00095 #define TUPLE_LOCK_READ(X) g_static_rw_lock_reader_lock(&tuple_rwlock) 00096 #define TUPLE_UNLOCK_READ(X) g_static_rw_lock_reader_unlock(&tuple_rwlock) 00097 00098 00099 /* iterative destructor of tuple values. */ 00100 static void 00101 tuple_value_destroy(mowgli_dictionary_elem_t *delem, gpointer privdata) 00102 { 00103 TupleValue *value = (TupleValue *) delem->data; 00104 00105 if (value->type == TUPLE_STRING) { 00106 stringpool_unref(value->value.string); 00107 value->value.string = NULL; 00108 } 00109 00110 mowgli_heap_free(tuple_value_heap, value); 00111 } 00112 00113 static void 00114 tuple_destroy(gpointer data) 00115 { 00116 Tuple *tuple = (Tuple *) data; 00117 gint i; 00118 00119 TUPLE_LOCK_WRITE(); 00120 mowgli_dictionary_destroy(tuple->dict, tuple_value_destroy, NULL); 00121 00122 for (i = 0; i < FIELD_LAST; i++) 00123 if (tuple->values[i]) { 00124 TupleValue *value = tuple->values[i]; 00125 00126 if (value->type == TUPLE_STRING) { 00127 stringpool_unref(value->value.string); 00128 value->value.string = NULL; 00129 } 00130 00131 mowgli_heap_free(tuple_value_heap, value); 00132 } 00133 00134 g_free(tuple->subtunes); 00135 00136 mowgli_heap_free(tuple_heap, tuple); 00137 TUPLE_UNLOCK_WRITE(); 00138 } 00139 00140 static Tuple * 00141 tuple_new_unlocked(void) 00142 { 00143 Tuple *tuple; 00144 00145 if (tuple_heap == NULL) 00146 { 00147 tuple_heap = mowgli_heap_create(sizeof(Tuple), 512, BH_NOW); 00148 tuple_value_heap = mowgli_heap_create(sizeof(TupleValue), 1024, BH_NOW); 00149 mowgli_object_class_init(&tuple_klass, "audacious.tuple", tuple_destroy, FALSE); 00150 } 00151 00152 /* FIXME: use mowgli_object_bless_from_class() in mowgli 0.4 00153 when it is released --nenolod */ 00154 tuple = mowgli_heap_alloc(tuple_heap); 00155 memset(tuple, 0, sizeof(Tuple)); 00156 mowgli_object_init(mowgli_object(tuple), NULL, &tuple_klass, NULL); 00157 00158 tuple->dict = mowgli_dictionary_create(g_ascii_strcasecmp); 00159 00160 return tuple; 00161 } 00162 00168 Tuple * 00169 tuple_new(void) 00170 { 00171 Tuple *tuple; 00172 00173 TUPLE_LOCK_WRITE(); 00174 00175 tuple = tuple_new_unlocked(); 00176 00177 TUPLE_UNLOCK_WRITE(); 00178 return tuple; 00179 } 00180 00181 static TupleValue * 00182 tuple_associate_data(Tuple *tuple, const gint cnfield, const gchar *field, TupleValueType ftype); 00183 00184 00193 void 00194 tuple_set_filename(Tuple *tuple, const gchar *filename) 00195 { 00196 gchar *local = g_strdup(filename); 00197 gchar *slash, *period, *question; 00198 00199 string_decode_percent(local); 00200 00201 /* Convert invalid UTF-8 URI's quietly. */ 00202 if (! g_utf8_validate (local, -1, NULL)) 00203 { 00204 gchar * utf8 = str_to_utf8 (local); 00205 g_free (local); 00206 local = utf8; 00207 } 00208 00209 slash = strrchr(local, '/'); 00210 period = strrchr(local, '.'); 00211 question = strrchr(local, '?'); 00212 00213 if (slash != NULL) 00214 { 00215 gchar temp = *(slash + 1); 00216 00217 *(slash + 1) = 0; 00218 tuple_associate_string(tuple, FIELD_FILE_PATH, NULL, local); 00219 *(slash + 1) = temp; 00220 tuple_associate_string(tuple, FIELD_FILE_NAME, NULL, slash + 1); 00221 } 00222 00223 if (question != NULL) 00224 { 00225 gint subtune; 00226 00227 *question = 0; 00228 00229 if (sscanf(question + 1, "%d", &subtune) == 1) 00230 tuple_associate_int(tuple, FIELD_SUBSONG_ID, NULL, subtune); 00231 } 00232 00233 if (period != NULL) 00234 tuple_associate_string(tuple, FIELD_FILE_EXT, NULL, period + 1); 00235 00236 g_free(local); 00237 } 00238 00246 static TupleValue * 00247 tuple_copy_value(TupleValue *src) 00248 { 00249 TupleValue *res; 00250 00251 if (src == NULL) return NULL; 00252 00253 res = mowgli_heap_alloc(tuple_value_heap); 00254 res->type = src->type; 00255 00256 switch (src->type) { 00257 case TUPLE_STRING: 00258 res->value.string = stringpool_get(src->value.string); 00259 break; 00260 case TUPLE_INT: 00261 res->value.integer = src->value.integer; 00262 break; 00263 default: 00264 mowgli_heap_free (tuple_value_heap, res); 00265 return NULL; 00266 } 00267 return res; 00268 } 00269 00276 Tuple * 00277 tuple_copy(const Tuple *src) 00278 { 00279 Tuple *dst; 00280 TupleValue * tv, * copied; 00281 mowgli_dictionary_iteration_state_t state; 00282 gint i; 00283 00284 g_return_val_if_fail(src != NULL, NULL); 00285 00286 TUPLE_LOCK_WRITE(); 00287 00288 dst = tuple_new_unlocked(); 00289 00290 /* Copy basic fields */ 00291 for (i = 0; i < FIELD_LAST; i++) 00292 dst->values[i] = tuple_copy_value(src->values[i]); 00293 00294 /* Copy dictionary contents */ 00295 MOWGLI_DICTIONARY_FOREACH (tv, & state, src->dict) 00296 { 00297 if ((copied = tuple_copy_value (tv)) != NULL) 00298 mowgli_dictionary_add (dst->dict, state.cur->key, copied); 00299 } 00300 00301 /* Copy subtune number information */ 00302 if (src->subtunes && src->nsubtunes > 0) 00303 { 00304 dst->nsubtunes = src->nsubtunes; 00305 dst->subtunes = g_new(gint, dst->nsubtunes); 00306 memcpy(dst->subtunes, src->subtunes, sizeof(gint) * dst->nsubtunes); 00307 } 00308 00309 TUPLE_UNLOCK_WRITE(); 00310 return dst; 00311 } 00312 00320 Tuple * 00321 tuple_new_from_filename(const gchar *filename) 00322 { 00323 Tuple *tuple = tuple_new(); 00324 00325 tuple_set_filename(tuple, filename); 00326 return tuple; 00327 } 00328 00329 00330 static gint 00331 tuple_get_nfield(const gchar *field) 00332 { 00333 gint i; 00334 for (i = 0; i < FIELD_LAST; i++) 00335 if (!strcmp(field, tuple_fields[i].name)) 00336 return i; 00337 return -1; 00338 } 00339 00340 00359 static TupleValue * 00360 tuple_associate_data(Tuple *tuple, const gint cnfield, const gchar *field, TupleValueType ftype) 00361 { 00362 const gchar *tfield = field; 00363 gint nfield = cnfield; 00364 TupleValue *value = NULL; 00365 00366 g_return_val_if_fail(tuple != NULL, NULL); 00367 g_return_val_if_fail(cnfield < FIELD_LAST, NULL); 00368 00369 /* Check for known fields */ 00370 if (nfield < 0) { 00371 nfield = tuple_get_nfield(field); 00372 if (nfield >= 0) 00373 g_warning("Tuple FIELD_* not used for '%s'!\n", field); 00374 } 00375 00376 /* Check if field was known */ 00377 if (nfield >= 0) { 00378 tfield = tuple_fields[nfield].name; 00379 value = tuple->values[nfield]; 00380 00381 if (ftype != tuple_fields[nfield].type) { 00382 g_warning("Invalid type for [%s](%d->%d), %d != %d\n", 00383 tfield, cnfield, nfield, ftype, tuple_fields[nfield].type); 00384 //mowgli_throw_exception_val(audacious.tuple.invalid_type_request, 0); 00385 TUPLE_UNLOCK_WRITE(); 00386 return NULL; 00387 } 00388 } else { 00389 value = mowgli_dictionary_retrieve(tuple->dict, tfield); 00390 } 00391 00392 if (value != NULL) { 00393 /* Value exists, just delete old associated data */ 00394 if (value->type == TUPLE_STRING) { 00395 stringpool_unref(value->value.string); 00396 value->value.string = NULL; 00397 } 00398 } else { 00399 /* Allocate a new value */ 00400 value = mowgli_heap_alloc(tuple_value_heap); 00401 value->type = ftype; 00402 if (nfield >= 0) 00403 tuple->values[nfield] = value; 00404 else 00405 mowgli_dictionary_add(tuple->dict, tfield, value); 00406 } 00407 00408 return value; 00409 } 00410 00424 gboolean 00425 tuple_associate_string(Tuple *tuple, const gint nfield, const gchar *field, const gchar *string) 00426 { 00427 TupleValue *value; 00428 00429 TUPLE_LOCK_WRITE(); 00430 if ((value = tuple_associate_data(tuple, nfield, field, TUPLE_STRING)) == NULL) 00431 return FALSE; 00432 00433 if (string == NULL) 00434 value->value.string = NULL; 00435 else if (g_utf8_validate (string, -1, NULL)) 00436 value->value.string = stringpool_get (string); 00437 else 00438 { 00439 fprintf (stderr, "Invalid UTF-8: %s.\n", string); 00440 gchar * copy = str_to_utf8 (string); 00441 value->value.string = stringpool_get (copy); 00442 g_free (copy); 00443 } 00444 00445 TUPLE_UNLOCK_WRITE(); 00446 return TRUE; 00447 } 00448 00464 gboolean tuple_associate_string_rel (Tuple * tuple, const gint nfield, 00465 const gchar * field, gchar * string) 00466 { 00467 gboolean ret = tuple_associate_string (tuple, nfield, field, string); 00468 g_free (string); 00469 return ret; 00470 } 00471 00485 gboolean 00486 tuple_associate_int(Tuple *tuple, const gint nfield, const gchar *field, gint integer) 00487 { 00488 TupleValue *value; 00489 00490 TUPLE_LOCK_WRITE(); 00491 if ((value = tuple_associate_data(tuple, nfield, field, TUPLE_INT)) == NULL) 00492 return FALSE; 00493 00494 value->value.integer = integer; 00495 00496 TUPLE_UNLOCK_WRITE(); 00497 return TRUE; 00498 } 00499 00509 void 00510 tuple_disassociate(Tuple *tuple, const gint cnfield, const gchar *field) 00511 { 00512 TupleValue *value; 00513 gint nfield = cnfield; 00514 00515 g_return_if_fail(tuple != NULL); 00516 g_return_if_fail(nfield < FIELD_LAST); 00517 00518 if (nfield < 0) 00519 nfield = tuple_get_nfield(field); 00520 00521 TUPLE_LOCK_WRITE(); 00522 if (nfield < 0) 00523 /* why _delete()? because _delete() returns the dictnode's data on success */ 00524 value = mowgli_dictionary_delete(tuple->dict, field); 00525 else { 00526 value = tuple->values[nfield]; 00527 tuple->values[nfield] = NULL; 00528 } 00529 00530 if (value == NULL) { 00531 TUPLE_UNLOCK_WRITE(); 00532 return; 00533 } 00534 00535 /* Free associated data */ 00536 if (value->type == TUPLE_STRING) { 00537 stringpool_unref(value->value.string); 00538 value->value.string = NULL; 00539 } 00540 00541 mowgli_heap_free(tuple_value_heap, value); 00542 TUPLE_UNLOCK_WRITE(); 00543 } 00544 00555 TupleValueType tuple_get_value_type (const Tuple * tuple, gint cnfield, 00556 const gchar * field) 00557 { 00558 TupleValueType type = TUPLE_UNKNOWN; 00559 gint nfield = cnfield; 00560 00561 g_return_val_if_fail(tuple != NULL, TUPLE_UNKNOWN); 00562 g_return_val_if_fail(nfield < FIELD_LAST, TUPLE_UNKNOWN); 00563 00564 if (nfield < 0) 00565 nfield = tuple_get_nfield(field); 00566 00567 TUPLE_LOCK_READ(); 00568 if (nfield < 0) { 00569 TupleValue *value; 00570 if ((value = mowgli_dictionary_retrieve(tuple->dict, field)) != NULL) 00571 type = value->type; 00572 } else { 00573 if (tuple->values[nfield]) 00574 type = tuple->values[nfield]->type; 00575 } 00576 00577 TUPLE_UNLOCK_READ(); 00578 return type; 00579 } 00580 00592 const gchar * tuple_get_string (const Tuple * tuple, gint cnfield, const gchar * 00593 field) 00594 { 00595 TupleValue *value; 00596 gint nfield = cnfield; 00597 00598 g_return_val_if_fail(tuple != NULL, NULL); 00599 g_return_val_if_fail(nfield < FIELD_LAST, NULL); 00600 00601 if (nfield < 0) 00602 nfield = tuple_get_nfield(field); 00603 00604 TUPLE_LOCK_READ(); 00605 if (nfield < 0) 00606 value = mowgli_dictionary_retrieve(tuple->dict, field); 00607 else 00608 value = tuple->values[nfield]; 00609 00610 if (value) { 00611 if (value->type != TUPLE_STRING) 00612 mowgli_throw_exception_val(audacious.tuple.invalid_type_request, NULL); 00613 00614 TUPLE_UNLOCK_READ(); 00615 return value->value.string; 00616 } else { 00617 TUPLE_UNLOCK_READ(); 00618 return NULL; 00619 } 00620 } 00621 00634 gint tuple_get_int (const Tuple * tuple, gint cnfield, const gchar * field) 00635 { 00636 TupleValue *value; 00637 gint nfield = cnfield; 00638 00639 g_return_val_if_fail(tuple != NULL, 0); 00640 g_return_val_if_fail(nfield < FIELD_LAST, 0); 00641 00642 if (nfield < 0) 00643 nfield = tuple_get_nfield(field); 00644 00645 TUPLE_LOCK_READ(); 00646 if (nfield < 0) 00647 value = mowgli_dictionary_retrieve(tuple->dict, field); 00648 else 00649 value = tuple->values[nfield]; 00650 00651 if (value) { 00652 if (value->type != TUPLE_INT) 00653 mowgli_throw_exception_val(audacious.tuple.invalid_type_request, 0); 00654 00655 TUPLE_UNLOCK_READ(); 00656 return value->value.integer; 00657 } else { 00658 TUPLE_UNLOCK_READ(); 00659 return 0; 00660 } 00661 }