XMMS2

src/lib/xmmstypes/value.c

Go to the documentation of this file.
00001 /*  XMMS2 - X Music Multiplexer System
00002  *  Copyright (C) 2003-2009 XMMS2 Team
00003  *
00004  *  PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
00005  *
00006  *  This library is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU Lesser General Public
00008  *  License as published by the Free Software Foundation; either
00009  *  version 2.1 of the License, or (at your option) any later version.
00010  *
00011  *  This library is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  *  Lesser General Public License for more details.
00015  */
00016 
00017 #include <stdio.h>
00018 #include <stdlib.h>
00019 #include <stdarg.h>
00020 #include <string.h>
00021 #include <ctype.h>
00022 #include <assert.h>
00023 
00024 #include "xmmsc/xmmsv.h"
00025 #include "xmmsc/xmmsc_idnumbers.h"
00026 #include "xmmsc/xmmsc_errorcodes.h"
00027 #include "xmmsc/xmmsc_stdbool.h"
00028 #include "xmmsc/xmmsc_util.h"
00029 #include "xmmspriv/xmms_list.h"
00030 
00031 
00032 /* Default source preferences for accessing "propdicts" */
00033 const char *default_source_pref[] = {
00034     "server",
00035     "client/*",
00036     "plugin/id3v2",
00037     "plugin/segment",
00038     "plugin/*",
00039     "*",
00040     NULL
00041 };
00042 
00043 
00044 typedef struct xmmsv_list_St xmmsv_list_t;
00045 typedef struct xmmsv_dict_St xmmsv_dict_t;
00046 
00047 
00048 typedef struct xmmsv_bin_St {
00049     unsigned char *data;
00050     uint32_t len;
00051 } xmmsv_bin_t;
00052 
00053 struct xmmsv_list_St {
00054     xmmsv_t **list;
00055     xmmsv_t *parent_value;
00056     int size;
00057     int allocated;
00058     bool restricted;
00059     xmmsv_type_t restricttype;
00060     x_list_t *iterators;
00061 };
00062 
00063 static xmmsv_list_t *xmmsv_list_new (void);
00064 static void xmmsv_list_free (xmmsv_list_t *l);
00065 static int xmmsv_list_resize (xmmsv_list_t *l, int newsize);
00066 static int _xmmsv_list_insert (xmmsv_list_t *l, int pos, xmmsv_t *val);
00067 static int _xmmsv_list_append (xmmsv_list_t *l, xmmsv_t *val);
00068 static int _xmmsv_list_remove (xmmsv_list_t *l, int pos);
00069 static int _xmmsv_list_move (xmmsv_list_t *l, int old_pos, int new_pos);
00070 static void _xmmsv_list_clear (xmmsv_list_t *l);
00071 
00072 static xmmsv_dict_t *xmmsv_dict_new (void);
00073 static void xmmsv_dict_free (xmmsv_dict_t *dict);
00074 
00075 
00076 struct xmmsv_list_iter_St {
00077     xmmsv_list_t *parent;
00078     int position;
00079 };
00080 
00081 static xmmsv_list_iter_t *xmmsv_list_iter_new (xmmsv_list_t *l);
00082 static void xmmsv_list_iter_free (xmmsv_list_iter_t *it);
00083 
00084 
00085 static xmmsv_dict_iter_t *xmmsv_dict_iter_new (xmmsv_dict_t *d);
00086 static void xmmsv_dict_iter_free (xmmsv_dict_iter_t *it);
00087 
00088 
00089 
00090 struct xmmsv_St {
00091     union {
00092         char *error;
00093         int32_t int32;
00094         char *string;
00095         xmmsv_coll_t *coll;
00096         xmmsv_bin_t bin;
00097         xmmsv_list_t *list;
00098         xmmsv_dict_t *dict;
00099     } value;
00100     xmmsv_type_t type;
00101 
00102     int ref;  /* refcounting */
00103 };
00104 
00105 
00106 static xmmsv_t *xmmsv_new (xmmsv_type_t type);
00107 static void xmmsv_free (xmmsv_t *val);
00108 static int absolutify_and_validate_pos (int *pos, int size, int allow_append);
00109 
00110 
00111 
00112 
00113 /**
00114  * @defgroup ValueType ValueType
00115  * @ingroup Values
00116  * @brief The API to be used to work with value objects.
00117  *
00118  * @{
00119  */
00120 
00121 /**
00122  * Allocates a new empty #xmmsv_t.
00123  * @return The new #xmmsv_t. Must be unreferenced with
00124  * #xmmsv_unref.
00125  */
00126 xmmsv_t *
00127 xmmsv_new_none (void)
00128 {
00129     xmmsv_t *val = xmmsv_new (XMMSV_TYPE_NONE);
00130     return val;
00131 }
00132 
00133 /**
00134  * Allocates a new error #xmmsv_t.
00135  * @param s The error message to store in the #xmmsv_t. The
00136  * string is copied in the value.
00137  * @return The new #xmmsv_t. Must be unreferenced with
00138  * #xmmsv_unref.
00139  */
00140 xmmsv_t *
00141 xmmsv_new_error (const char *errstr)
00142 {
00143     xmmsv_t *val = xmmsv_new (XMMSV_TYPE_ERROR);
00144 
00145     if (val) {
00146         val->value.error = strdup (errstr);
00147     }
00148 
00149     return val;
00150 }
00151 
00152 /**
00153  * Allocates a new integer #xmmsv_t.
00154  * @param i The value to store in the #xmmsv_t.
00155  * @return The new #xmmsv_t. Must be unreferenced with
00156  * #xmmsv_unref.
00157  */
00158 xmmsv_t *
00159 xmmsv_new_int (int32_t i)
00160 {
00161     xmmsv_t *val = xmmsv_new (XMMSV_TYPE_INT32);
00162 
00163     if (val) {
00164         val->value.int32 = i;
00165     }
00166 
00167     return val;
00168 }
00169 
00170 /**
00171  * Allocates a new string #xmmsv_t.
00172  * @param s The value to store in the #xmmsv_t. The string is
00173  * copied in the value.
00174  * @return The new #xmmsv_t. Must be unreferenced with
00175  * #xmmsv_unref.
00176  */
00177 xmmsv_t *
00178 xmmsv_new_string (const char *s)
00179 {
00180     xmmsv_t *val;
00181 
00182     x_return_val_if_fail (s, NULL);
00183     x_return_val_if_fail (xmmsv_utf8_validate (s), NULL);
00184 
00185     val = xmmsv_new (XMMSV_TYPE_STRING);
00186     if (val) {
00187         val->value.string = strdup (s);
00188     }
00189 
00190     return val;
00191 }
00192 
00193 /**
00194  * Allocates a new collection #xmmsv_t.
00195  * @param s The value to store in the #xmmsv_t.
00196  * @return The new #xmmsv_t. Must be unreferenced with
00197  * #xmmsv_unref.
00198  */
00199 xmmsv_t *
00200 xmmsv_new_coll (xmmsv_coll_t *c)
00201 {
00202     xmmsv_t *val;
00203 
00204     x_return_val_if_fail (c, NULL);
00205 
00206     val = xmmsv_new (XMMSV_TYPE_COLL);
00207     if (val) {
00208         val->value.coll = c;
00209         xmmsv_coll_ref (c);
00210     }
00211 
00212     return val;
00213 }
00214 
00215 /**
00216  * Allocates a new binary data #xmmsv_t.
00217  * @param data The data to store in the #xmmsv_t.
00218  * @param len The size of the data.
00219  * @return The new #xmmsv_t. Must be unreferenced with
00220  * #xmmsv_unref.
00221  */
00222 xmmsv_t *
00223 xmmsv_new_bin (unsigned char *data, unsigned int len)
00224 {
00225     xmmsv_t *val = xmmsv_new (XMMSV_TYPE_BIN);
00226 
00227     if (val) {
00228         /* copy the data! */
00229         val->value.bin.data = x_malloc (len);
00230         if (!val->value.bin.data) {
00231             free (val);
00232             x_oom ();
00233             return NULL;
00234         }
00235         memcpy (val->value.bin.data, data, len);
00236         val->value.bin.len = len;
00237     }
00238 
00239     return val;
00240 }
00241 
00242 /**
00243  * Allocates a new list #xmmsv_t.
00244  * @return The new #xmmsv_t. Must be unreferenced with
00245  * #xmmsv_unref.
00246  */
00247 xmmsv_t *
00248 xmmsv_new_list (void)
00249 {
00250     xmmsv_t *val = xmmsv_new (XMMSV_TYPE_LIST);
00251 
00252     if (val) {
00253         val->value.list = xmmsv_list_new ();
00254         val->value.list->parent_value = val;
00255     }
00256 
00257     return val;
00258 }
00259 
00260 /**
00261  * Allocates a new dict #xmmsv_t.
00262  * @return The new #xmmsv_t. Must be unreferenced with
00263  * #xmmsv_unref.
00264  */
00265 xmmsv_t *
00266 xmmsv_new_dict (void)
00267 {
00268     xmmsv_t *val = xmmsv_new (XMMSV_TYPE_DICT);
00269 
00270     if (val) {
00271         val->value.dict = xmmsv_dict_new ();
00272     }
00273 
00274     return val;
00275 }
00276 
00277 
00278 
00279 /**
00280  * References the #xmmsv_t
00281  *
00282  * @param val the value to reference.
00283  * @return val
00284  */
00285 xmmsv_t *
00286 xmmsv_ref (xmmsv_t *val)
00287 {
00288     x_return_val_if_fail (val, NULL);
00289     val->ref++;
00290 
00291     return val;
00292 }
00293 
00294 /**
00295  * Decreases the references for the #xmmsv_t
00296  * When the number of references reaches 0 it will
00297  * be freed. And thus all data you extracted from it
00298  * will be deallocated.
00299  */
00300 void
00301 xmmsv_unref (xmmsv_t *val)
00302 {
00303     x_return_if_fail (val);
00304     x_api_error_if (val->ref < 1, "with a freed value",);
00305 
00306     val->ref--;
00307     if (val->ref == 0) {
00308         xmmsv_free (val);
00309     }
00310 }
00311 
00312 
00313 /**
00314  * Allocates new #xmmsv_t and references it.
00315  * @internal
00316  */
00317 static xmmsv_t *
00318 xmmsv_new (xmmsv_type_t type)
00319 {
00320     xmmsv_t *val;
00321 
00322     val = x_new0 (xmmsv_t, 1);
00323     if (!val) {
00324         x_oom ();
00325         return NULL;
00326     }
00327 
00328     val->type = type;
00329 
00330     return xmmsv_ref (val);
00331 }
00332 
00333 /**
00334  * Free a #xmmsv_t along with its internal data.
00335  * @internal
00336  */
00337 static void
00338 xmmsv_free (xmmsv_t *val)
00339 {
00340     x_return_if_fail (val);
00341 
00342     switch (val->type) {
00343         case XMMSV_TYPE_NONE :
00344         case XMMSV_TYPE_END :
00345         case XMMSV_TYPE_INT32 :
00346             break;
00347         case XMMSV_TYPE_ERROR :
00348             free (val->value.error);
00349             val->value.error = NULL;
00350             break;
00351         case XMMSV_TYPE_STRING :
00352             free (val->value.string);
00353             val->value.string = NULL;
00354             break;
00355         case XMMSV_TYPE_COLL:
00356             xmmsv_coll_unref (val->value.coll);
00357             val->value.coll = NULL;
00358             break;
00359         case XMMSV_TYPE_BIN :
00360             free (val->value.bin.data);
00361             val->value.bin.len = 0;
00362             break;
00363         case XMMSV_TYPE_LIST:
00364             xmmsv_list_free (val->value.list);
00365             val->value.list = NULL;
00366             break;
00367         case XMMSV_TYPE_DICT:
00368             xmmsv_dict_free (val->value.dict);
00369             val->value.dict = NULL;
00370             break;
00371     }
00372 
00373     free (val);
00374 }
00375 
00376 
00377 /**
00378  * Get the type of the value.
00379  *
00380  * @param val a #xmmsv_t to get the type from.
00381  * @returns The data type in the value.
00382  */
00383 xmmsv_type_t
00384 xmmsv_get_type (const xmmsv_t *val)
00385 {
00386     x_api_error_if (!val, "NULL value",
00387                     XMMSV_TYPE_NONE);
00388 
00389     return val->type;
00390 }
00391 
00392 /**
00393  * Check if value is of specified type.
00394  *
00395  * @param val #xmmsv_t to check.
00396  * @param t #xmmsv_type_t to check for.
00397  * @return 1 if value is of specified type, 0 otherwise.
00398  */
00399 int
00400 xmmsv_is_type (const xmmsv_t *val, xmmsv_type_t t)
00401 {
00402     x_api_error_if (!val, "NULL value", 0);
00403 
00404     return (xmmsv_get_type (val) == t);
00405 }
00406 
00407 
00408 /* Merely legacy aliases */
00409 
00410 /**
00411  * Check if the value stores an error.
00412  *
00413  * @param val a #xmmsv_t
00414  * @return 1 if error was encountered, 0 otherwise.
00415  */
00416 int
00417 xmmsv_is_error (const xmmsv_t *val)
00418 {
00419     return xmmsv_is_type (val, XMMSV_TYPE_ERROR);
00420 }
00421 
00422 /**
00423  * Check if the value stores a list.
00424  *
00425  * @param val a #xmmsv_t
00426  * @return 1 if value stores a list, 0 otherwise.
00427  */
00428 int
00429 xmmsv_is_list (const xmmsv_t *val)
00430 {
00431     return xmmsv_is_type (val, XMMSV_TYPE_LIST);
00432 }
00433 
00434 /**
00435  * Check if the value stores a dict.
00436  *
00437  * @param val a #xmmsv_t
00438  * @return 1 if value stores a dict, 0 otherwise.
00439  */
00440 int
00441 xmmsv_is_dict (const xmmsv_t *val)
00442 {
00443     return xmmsv_is_type (val, XMMSV_TYPE_DICT);
00444 }
00445 
00446 /**
00447  * Legacy alias to retrieve the error string from an
00448  * #xmmsv_t. Obsolete now, use #xmmsv_get_error instead!
00449  *
00450  * @param val an error #xmmsv_t
00451  * @return the error string if valid, NULL otherwise.
00452  */
00453 const char *
00454 xmmsv_get_error_old (const xmmsv_t *val)
00455 {
00456     if (!val || val->type != XMMSV_TYPE_ERROR) {
00457         return NULL;
00458     }
00459 
00460     return val->value.error;
00461 }
00462 
00463 /**
00464  * Helper function to build a list #xmmsv_t containing the
00465  * strings from the input array.
00466  *
00467  * @param array An array of C strings. Must be NULL-terminated if num
00468  *              is -1.
00469  * @param num The optional number of elements to read from the array. Set to
00470  *            -1 if the array is NULL-terminated.
00471  * @return An #xmmsv_t containing the list of strings. Must be
00472  *         unreffed manually when done.
00473  */
00474 xmmsv_t *
00475 xmmsv_make_stringlist (char *array[], int num)
00476 {
00477     xmmsv_t *list, *elem;
00478     int i;
00479 
00480     list = xmmsv_new_list ();
00481     if (array) {
00482         for (i = 0; (num >= 0 && i < num) || array[i]; i++) {
00483             elem = xmmsv_new_string (array[i]);
00484             xmmsv_list_append (list, elem);
00485             xmmsv_unref (elem);
00486         }
00487     }
00488 
00489     return list;
00490 }
00491 
00492 /**
00493  * Gets the type of a dict entry.
00494  *
00495  * @param val A xmmsv_t containing a dict.
00496  * @param key The key in the dict.
00497  * @return The type of the entry or #XMMSV_TYPE_NONE if something goes wrong.
00498  */
00499 xmmsv_type_t
00500 xmmsv_dict_entry_get_type (xmmsv_t *val, const char *key)
00501 {
00502     xmmsv_t *v;
00503 
00504     if (!xmmsv_dict_get (val, key, &v)) {
00505         return XMMSV_TYPE_NONE;
00506     }
00507 
00508     return xmmsv_get_type (v);
00509 }
00510 
00511 
00512 /* macro-magically define dict extractors */
00513 #define GEN_DICT_EXTRACTOR_FUNC(typename, type)         \
00514     int                             \
00515     xmmsv_dict_entry_get_##typename (xmmsv_t *val, const char *key, \
00516                                      type *r)           \
00517     {                               \
00518         xmmsv_t *v;                     \
00519         if (!xmmsv_dict_get (val, key, &v)) {           \
00520             return 0;                   \
00521         }                           \
00522         return xmmsv_get_##typename (v, r);         \
00523     }
00524 
00525 GEN_DICT_EXTRACTOR_FUNC (string, const char *)
00526 GEN_DICT_EXTRACTOR_FUNC (int, int32_t)
00527 GEN_DICT_EXTRACTOR_FUNC (coll, xmmsv_coll_t *)
00528 
00529 /* macro-magically define dict set functions */
00530 #define GEN_DICT_SET_FUNC(typename, type) \
00531     int \
00532     xmmsv_dict_set_##typename (xmmsv_t *dict, const char *key, type elem) \
00533     { \
00534         int ret; \
00535         xmmsv_t *v; \
00536  \
00537         v = xmmsv_new_##typename (elem); \
00538         ret = xmmsv_dict_set (dict, key, v); \
00539         xmmsv_unref (v); \
00540  \
00541         return ret; \
00542     }
00543 
00544 GEN_DICT_SET_FUNC (string, const char *)
00545 GEN_DICT_SET_FUNC (int, int32_t)
00546 GEN_DICT_SET_FUNC (coll, xmmsv_coll_t *)
00547 
00548 /* macro-magically define dict_iter extractors */
00549 #define GEN_DICT_ITER_EXTRACTOR_FUNC(typename, type) \
00550     int \
00551     xmmsv_dict_iter_pair_##typename (xmmsv_dict_iter_t *it, \
00552                                      const char **key, \
00553                                      type *r) \
00554     { \
00555         xmmsv_t *v; \
00556         if (!xmmsv_dict_iter_pair (it, key, &v)) { \
00557             return 0; \
00558         } \
00559         if (r) { \
00560             return xmmsv_get_##typename (v, r); \
00561         } else { \
00562             return 1; \
00563         } \
00564     }
00565 
00566 GEN_DICT_ITER_EXTRACTOR_FUNC (string, const char *)
00567 GEN_DICT_ITER_EXTRACTOR_FUNC (int, int32_t)
00568 GEN_DICT_ITER_EXTRACTOR_FUNC (coll, xmmsv_coll_t *)
00569 
00570 /* macro-magically define dict_iter set functions */
00571 #define GEN_DICT_ITER_SET_FUNC(typename, type) \
00572     int \
00573     xmmsv_dict_iter_set_##typename (xmmsv_dict_iter_t *it, type elem) \
00574     { \
00575         int ret; \
00576         xmmsv_t *v; \
00577  \
00578         v = xmmsv_new_##typename (elem); \
00579         ret = xmmsv_dict_iter_set (it, v); \
00580         xmmsv_unref (v); \
00581  \
00582         return ret; \
00583     }
00584 
00585 GEN_DICT_ITER_SET_FUNC (string, const char *)
00586 GEN_DICT_ITER_SET_FUNC (int, int32_t)
00587 GEN_DICT_ITER_SET_FUNC (coll, xmmsv_coll_t *)
00588 
00589 /* macro-magically define list extractors */
00590 #define GEN_LIST_EXTRACTOR_FUNC(typename, type) \
00591     int \
00592     xmmsv_list_get_##typename (xmmsv_t *val, int pos, type *r) \
00593     { \
00594         xmmsv_t *v; \
00595         if (!xmmsv_list_get (val, pos, &v)) { \
00596             return 0; \
00597         } \
00598         return xmmsv_get_##typename (v, r); \
00599     }
00600 
00601 GEN_LIST_EXTRACTOR_FUNC (string, const char *)
00602 GEN_LIST_EXTRACTOR_FUNC (int, int32_t)
00603 GEN_LIST_EXTRACTOR_FUNC (coll, xmmsv_coll_t *)
00604 
00605 /* macro-magically define list set functions */
00606 #define GEN_LIST_SET_FUNC(typename, type) \
00607     int \
00608     xmmsv_list_set_##typename (xmmsv_t *list, int pos, type elem) \
00609     { \
00610         int ret; \
00611         xmmsv_t *v; \
00612  \
00613         v = xmmsv_new_##typename (elem); \
00614         ret = xmmsv_list_set (list, pos, v); \
00615         xmmsv_unref (v); \
00616  \
00617         return ret; \
00618     }
00619 
00620 GEN_LIST_SET_FUNC (string, const char *)
00621 GEN_LIST_SET_FUNC (int, int32_t)
00622 GEN_LIST_SET_FUNC (coll, xmmsv_coll_t *)
00623 
00624 /* macro-magically define list insert functions */
00625 #define GEN_LIST_INSERT_FUNC(typename, type) \
00626     int \
00627     xmmsv_list_insert_##typename (xmmsv_t *list, int pos, type elem) \
00628     { \
00629         int ret; \
00630         xmmsv_t *v; \
00631  \
00632         v = xmmsv_new_##typename (elem); \
00633         ret = xmmsv_list_insert (list, pos, v); \
00634         xmmsv_unref (v); \
00635  \
00636         return ret; \
00637     }
00638 
00639 GEN_LIST_INSERT_FUNC (string, const char *)
00640 GEN_LIST_INSERT_FUNC (int, int32_t)
00641 GEN_LIST_INSERT_FUNC (coll, xmmsv_coll_t *)
00642 
00643 /* macro-magically define list append functions */
00644 #define GEN_LIST_APPEND_FUNC(typename, type) \
00645     int \
00646     xmmsv_list_append_##typename (xmmsv_t *list, type elem) \
00647     { \
00648         int ret; \
00649         xmmsv_t *v; \
00650  \
00651         v = xmmsv_new_##typename (elem); \
00652         ret = xmmsv_list_append (list, v); \
00653         xmmsv_unref (v); \
00654  \
00655         return ret; \
00656     }
00657 
00658 GEN_LIST_APPEND_FUNC (string, const char *)
00659 GEN_LIST_APPEND_FUNC (int, int32_t)
00660 GEN_LIST_APPEND_FUNC (coll, xmmsv_coll_t *)
00661 
00662 /* macro-magically define list_iter extractors */
00663 #define GEN_LIST_ITER_EXTRACTOR_FUNC(typename, type) \
00664     int \
00665     xmmsv_list_iter_entry_##typename (xmmsv_list_iter_t *it, type *r) \
00666     { \
00667         xmmsv_t *v; \
00668         if (!xmmsv_list_iter_entry (it, &v)) { \
00669             return 0; \
00670         } \
00671         return xmmsv_get_##typename (v, r); \
00672     }
00673 
00674 GEN_LIST_ITER_EXTRACTOR_FUNC (string, const char *)
00675 GEN_LIST_ITER_EXTRACTOR_FUNC (int, int32_t)
00676 GEN_LIST_ITER_EXTRACTOR_FUNC (coll, xmmsv_coll_t *)
00677 
00678 /* macro-magically define list_iter insert functions */
00679 #define GEN_LIST_ITER_INSERT_FUNC(typename, type) \
00680     int \
00681     xmmsv_list_iter_insert_##typename (xmmsv_list_iter_t *it, type elem) \
00682     { \
00683         int ret; \
00684         xmmsv_t *v; \
00685  \
00686         v = xmmsv_new_##typename (elem); \
00687         ret = xmmsv_list_iter_insert (it, v); \
00688         xmmsv_unref (v); \
00689  \
00690         return ret; \
00691     }
00692 
00693 GEN_LIST_ITER_INSERT_FUNC (string, const char *)
00694 GEN_LIST_ITER_INSERT_FUNC (int, int32_t)
00695 GEN_LIST_ITER_INSERT_FUNC (coll, xmmsv_coll_t *)
00696 
00697 static int
00698 source_match_pattern (const char *source, const char *pattern)
00699 {
00700     int match = 0;
00701     int lpos = strlen (pattern) - 1;
00702 
00703     if (strcasecmp (pattern, source) == 0) {
00704         match = 1;
00705     } else if (lpos >= 0 && pattern[lpos] == '*' &&
00706                (lpos == 0 || strncasecmp (source, pattern, lpos) == 0)) {
00707         match = 1;
00708     }
00709 
00710     return match;
00711 }
00712 
00713 /* Return the index of the source in the source prefs list, or -1 if
00714  * no match.
00715  */
00716 static int
00717 find_match_index (const char *source, const char **src_prefs)
00718 {
00719     int i, match = -1;
00720 
00721     for (i = 0; src_prefs[i]; i++) {
00722         if (source_match_pattern (source, src_prefs[i])) {
00723             match = i;
00724             break;
00725         }
00726     }
00727 
00728     return match;
00729 }
00730 
00731 /**
00732  * Helper function to transform a key-source-value dict-of-dict
00733  * #xmmsv_t (formerly a propdict) to a regular key-value dict, given a
00734  * list of source preference.
00735  *
00736  * @param propdict A key-source-value dict-of-dict #xmmsv_t.
00737  * @param src_prefs A list of source names or patterns. Must be
00738  *                  NULL-terminated. If this argument is NULL, the
00739  *                  default source preferences is used.
00740  * @return An #xmmsv_t containing a simple key-value dict. Must be
00741  *         unreffed manually when done.
00742  */
00743 xmmsv_t *
00744 xmmsv_propdict_to_dict (xmmsv_t *propdict, const char **src_prefs)
00745 {
00746     xmmsv_t *dict, *source_dict, *value, *best_value;
00747     xmmsv_dict_iter_t *key_it, *source_it;
00748     const char *key, *source;
00749     const char **local_prefs;
00750     int match_index, best_index;
00751 
00752     dict = xmmsv_new_dict ();
00753 
00754     local_prefs = src_prefs ? src_prefs : default_source_pref;
00755 
00756     xmmsv_get_dict_iter (propdict, &key_it);
00757     while (xmmsv_dict_iter_valid (key_it)) {
00758         xmmsv_dict_iter_pair (key_it, &key, &source_dict);
00759 
00760         best_value = NULL;
00761         best_index = -1;
00762         xmmsv_get_dict_iter (source_dict, &source_it);
00763         while (xmmsv_dict_iter_valid (source_it)) {
00764             xmmsv_dict_iter_pair (source_it, &source, &value);
00765             match_index = find_match_index (source, local_prefs);
00766             /* keep first match or better match */
00767             if (match_index >= 0 && (best_index < 0 ||
00768                                      match_index < best_index)) {
00769                 best_value = value;
00770                 best_index = match_index;
00771             }
00772             xmmsv_dict_iter_next (source_it);
00773         }
00774 
00775         /* Note: we do not insert a key-value pair if no source matches */
00776         if (best_value) {
00777             xmmsv_dict_set (dict, key, best_value);
00778         }
00779 
00780         xmmsv_dict_iter_next (key_it);
00781     }
00782 
00783     return dict;
00784 }
00785 
00786 
00787 /**
00788  * Retrieves an error string describing the server error from the
00789  * value.
00790  *
00791  * @param val a #xmmsv_t containing a integer.
00792  * @param r the return error.
00793  * @return 1 upon success otherwise 0
00794  */
00795 int
00796 xmmsv_get_error (const xmmsv_t *val, const char **r)
00797 {
00798     if (!val || val->type != XMMSV_TYPE_ERROR) {
00799         return 0;
00800     }
00801 
00802     *r = val->value.error;
00803 
00804     return 1;
00805 }
00806 
00807 /**
00808  * Retrieves a signed integer from the value.
00809  *
00810  * @param val a #xmmsv_t containing an integer.
00811  * @param r the return integer.
00812  * @return 1 upon success otherwise 0
00813  */
00814 int
00815 xmmsv_get_int (const xmmsv_t *val, int32_t *r)
00816 {
00817     if (!val || val->type != XMMSV_TYPE_INT32) {
00818         return 0;
00819     }
00820 
00821     *r = val->value.int32;
00822 
00823     return 1;
00824 }
00825 
00826 /**
00827  * Retrieves a unsigned integer from the value.
00828  *
00829  * @param val a #xmmsv_t containing an unsigned integer.
00830  * @param r the return unsigned integer.
00831  * @return 1 upon success otherwise 0
00832  */
00833 int
00834 xmmsv_get_uint (const xmmsv_t *val, uint32_t *r)
00835 {
00836     if (!val)
00837         return 0;
00838     if (val->type != XMMSV_TYPE_INT32)
00839         return 0;
00840 
00841     *r = val->value.int32;
00842 
00843     return 1;
00844 }
00845 
00846 /**
00847  * Retrieves a string from the value.
00848  *
00849  * @param val a #xmmsv_t containing a string.
00850  * @param r the return string. This string is owned by the value and
00851  * will be freed when the value is freed.
00852  * @return 1 upon success otherwise 0
00853  */
00854 int
00855 xmmsv_get_string (const xmmsv_t *val, const char **r)
00856 {
00857     if (!val || val->type != XMMSV_TYPE_STRING) {
00858         return 0;
00859     }
00860 
00861     *r = val->value.string;
00862 
00863     return 1;
00864 }
00865 
00866 /**
00867  * Retrieves a collection from the value.
00868  *
00869  * @param val a #xmmsv_t containing a collection.
00870  * @param c the return collection. This collection is owned by the
00871  * value and will be unref'd when the value is freed.
00872  * @return 1 upon success otherwise 0
00873  */
00874 int
00875 xmmsv_get_coll (const xmmsv_t *val, xmmsv_coll_t **c)
00876 {
00877     if (!val || val->type != XMMSV_TYPE_COLL) {
00878         return 0;
00879     }
00880 
00881     *c = val->value.coll;
00882 
00883     return 1;
00884 }
00885 
00886 /**
00887  * Retrieves binary data from the value.
00888  *
00889  * @param val a #xmmsv_t containing a string.
00890  * @param r the return data. This data is owned by the value and will
00891  * be freed when the value is freed.
00892  * @param rlen the return length of data.
00893  * @return 1 upon success otherwise 0
00894  */
00895 int
00896 xmmsv_get_bin (const xmmsv_t *val, const unsigned char **r, unsigned int *rlen)
00897 {
00898     if (!val || val->type != XMMSV_TYPE_BIN) {
00899         return 0;
00900     }
00901 
00902     *r = val->value.bin.data;
00903     *rlen = val->value.bin.len;
00904 
00905     return 1;
00906 }
00907 
00908 
00909 /**
00910  * Retrieves a list iterator from a list #xmmsv_t.
00911  *
00912  * @param val a #xmmsv_t containing a list.
00913  * @param it An #xmmsv_list_iter_t that can be used to access the list
00914  *           data. The iterator will be freed when the value is freed.
00915  * @return 1 upon success otherwise 0
00916  */
00917 int
00918 xmmsv_get_list_iter (const xmmsv_t *val, xmmsv_list_iter_t **it)
00919 {
00920     xmmsv_list_iter_t *new_it;
00921 
00922     if (!val || val->type != XMMSV_TYPE_LIST) {
00923         *it = NULL;
00924         return 0;
00925     }
00926 
00927     new_it = xmmsv_list_iter_new (val->value.list);
00928     if (!new_it) {
00929         *it = NULL;
00930         return 0;
00931     }
00932 
00933     *it = new_it;
00934 
00935     return 1;
00936 }
00937 
00938 /**
00939  * Retrieves a dict iterator from a dict #xmmsv_t.
00940  *
00941  * @param val a #xmmsv_t containing a dict.
00942  * @param it An #xmmsv_dict_iter_t that can be used to access the dict
00943  *           data. The iterator will be freed when the value is freed.
00944  * @return 1 upon success otherwise 0
00945  */
00946 int
00947 xmmsv_get_dict_iter (const xmmsv_t *val, xmmsv_dict_iter_t **it)
00948 {
00949     xmmsv_dict_iter_t *new_it;
00950 
00951     if (!val || val->type != XMMSV_TYPE_DICT) {
00952         *it = NULL;
00953         return 0;
00954     }
00955 
00956     new_it = xmmsv_dict_iter_new (val->value.dict);
00957     if (!new_it) {
00958         *it = NULL;
00959         return 0;
00960     }
00961 
00962     *it = new_it;
00963 
00964     return 1;
00965 }
00966 
00967 
00968 /* List stuff */
00969 
00970 static xmmsv_list_t *
00971 xmmsv_list_new (void)
00972 {
00973     xmmsv_list_t *list;
00974 
00975     list = x_new0 (xmmsv_list_t, 1);
00976     if (!list) {
00977         x_oom ();
00978         return NULL;
00979     }
00980 
00981     /* list is all empty for now! */
00982 
00983     return list;
00984 }
00985 
00986 static void
00987 xmmsv_list_free (xmmsv_list_t *l)
00988 {
00989     xmmsv_list_iter_t *it;
00990     int i;
00991 
00992     /* free iterators */
00993     while (l->iterators) {
00994         it = (xmmsv_list_iter_t *) l->iterators->data;
00995         xmmsv_list_iter_free (it);
00996     }
00997 
00998     /* unref contents */
00999     for (i = 0; i < l->size; i++) {
01000         xmmsv_unref (l->list[i]);
01001     }
01002 
01003     free (l->list);
01004     free (l);
01005 }
01006 
01007 static int
01008 xmmsv_list_resize (xmmsv_list_t *l, int newsize)
01009 {
01010     xmmsv_t **newmem;
01011 
01012     newmem = realloc (l->list, newsize * sizeof (xmmsv_t *));
01013 
01014     if (newsize != 0 && newmem == NULL) {
01015         x_oom ();
01016         return 0;
01017     }
01018 
01019     l->list = newmem;
01020     l->allocated = newsize;
01021 
01022     return 1;
01023 }
01024 
01025 static int
01026 _xmmsv_list_insert (xmmsv_list_t *l, int pos, xmmsv_t *val)
01027 {
01028     xmmsv_list_iter_t *it;
01029     x_list_t *n;
01030 
01031     if (!absolutify_and_validate_pos (&pos, l->size, 1)) {
01032         return 0;
01033     }
01034 
01035     if (l->restricted) {
01036         x_return_val_if_fail (xmmsv_is_type (val, l->restricttype), 0);
01037     }
01038 
01039     /* We need more memory, reallocate */
01040     if (l->size == l->allocated) {
01041         int success;
01042         size_t double_size;
01043         if (l->allocated > 0) {
01044             double_size = l->allocated << 1;
01045         } else {
01046             double_size = 1;
01047         }
01048         success = xmmsv_list_resize (l, double_size);
01049         x_return_val_if_fail (success, 0);
01050     }
01051 
01052     /* move existing items out of the way */
01053     if (l->size > pos) {
01054         memmove (l->list + pos + 1, l->list + pos,
01055                  (l->size - pos) * sizeof (xmmsv_t *));
01056     }
01057 
01058     l->list[pos] = xmmsv_ref (val);
01059     l->size++;
01060 
01061     /* update iterators pos */
01062     for (n = l->iterators; n; n = n->next) {
01063         it = (xmmsv_list_iter_t *) n->data;
01064         if (it->position > pos) {
01065             it->position++;
01066         }
01067     }
01068 
01069     return 1;
01070 }
01071 
01072 static int
01073 _xmmsv_list_append (xmmsv_list_t *l, xmmsv_t *val)
01074 {
01075     return _xmmsv_list_insert (l, l->size, val);
01076 }
01077 
01078 static int
01079 _xmmsv_list_remove (xmmsv_list_t *l, int pos)
01080 {
01081     xmmsv_list_iter_t *it;
01082     int half_size;
01083     x_list_t *n;
01084 
01085     /* prevent removing after the last element */
01086     if (!absolutify_and_validate_pos (&pos, l->size, 0)) {
01087         return 0;
01088     }
01089 
01090     xmmsv_unref (l->list[pos]);
01091 
01092     l->size--;
01093 
01094     /* fill the gap */
01095     if (pos < l->size) {
01096         memmove (l->list + pos, l->list + pos + 1,
01097                  (l->size - pos) * sizeof (xmmsv_t *));
01098     }
01099 
01100     /* Reduce memory usage by two if possible */
01101     half_size = l->allocated >> 1;
01102     if (l->size <= half_size) {
01103         int success;
01104         success = xmmsv_list_resize (l, half_size);
01105         x_return_val_if_fail (success, 0);
01106     }
01107 
01108     /* update iterator pos */
01109     for (n = l->iterators; n; n = n->next) {
01110         it = (xmmsv_list_iter_t *) n->data;
01111         if (it->position > pos) {
01112             it->position--;
01113         }
01114     }
01115 
01116     return 1;
01117 }
01118 
01119 static int
01120 _xmmsv_list_move (xmmsv_list_t *l, int old_pos, int new_pos)
01121 {
01122     xmmsv_t *v;
01123     xmmsv_list_iter_t *it;
01124     x_list_t *n;
01125 
01126     if (!absolutify_and_validate_pos (&old_pos, l->size, 0)) {
01127         return 0;
01128     }
01129     if (!absolutify_and_validate_pos (&new_pos, l->size, 0)) {
01130         return 0;
01131     }
01132 
01133     v = l->list[old_pos];
01134     if (old_pos < new_pos) {
01135         memmove (l->list + old_pos, l->list + old_pos + 1,
01136                  (new_pos - old_pos) * sizeof (xmmsv_t *));
01137         l->list[new_pos] = v;
01138 
01139         /* update iterator pos */
01140         for (n = l->iterators; n; n = n->next) {
01141             it = (xmmsv_list_iter_t *) n->data;
01142             if (it->position >= old_pos && it->position <= new_pos) {
01143                 if (it->position == old_pos) {
01144                     it->position = new_pos;
01145                 } else {
01146                     it->position--;
01147                 }
01148             }
01149         }
01150     } else {
01151         memmove (l->list + new_pos + 1, l->list + new_pos,
01152                  (old_pos - new_pos) * sizeof (xmmsv_t *));
01153         l->list[new_pos] = v;
01154 
01155         /* update iterator pos */
01156         for (n = l->iterators; n; n = n->next) {
01157             it = (xmmsv_list_iter_t *) n->data;
01158             if (it->position >= new_pos && it->position <= old_pos) {
01159                 if (it->position == old_pos) {
01160                     it->position = new_pos;
01161                 } else {
01162                     it->position++;
01163                 }
01164             }
01165         }
01166     }
01167 
01168     return 1;
01169 }
01170 
01171 static void
01172 _xmmsv_list_clear (xmmsv_list_t *l)
01173 {
01174     xmmsv_list_iter_t *it;
01175     x_list_t *n;
01176     int i;
01177 
01178     /* unref all stored values */
01179     for (i = 0; i < l->size; i++) {
01180         xmmsv_unref (l->list[i]);
01181     }
01182 
01183     /* free list, declare empty */
01184     free (l->list);
01185     l->list = NULL;
01186 
01187     l->size = 0;
01188     l->allocated = 0;
01189 
01190     /* reset iterator pos */
01191     for (n = l->iterators; n; n = n->next) {
01192         it = (xmmsv_list_iter_t *) n->data;
01193         it->position = 0;
01194     }
01195 }
01196 
01197 /**
01198  * Get the element at the given position in the list #xmmsv_t. This
01199  * function does not increase the refcount of the element, the
01200  * reference is still owned by the list.
01201  *
01202  * @param listv A #xmmsv_t containing a list.
01203  * @param pos The position in the list. If negative, start counting
01204  *            from the end (-1 is the last element, etc).
01205  * @param val Pointer set to a borrowed reference to the element at
01206  *            the given position in the list.
01207  * @return 1 upon success otherwise 0
01208  */
01209 int
01210 xmmsv_list_get (xmmsv_t *listv, int pos, xmmsv_t **val)
01211 {
01212     xmmsv_list_t *l;
01213 
01214     x_return_val_if_fail (listv, 0);
01215     x_return_val_if_fail (xmmsv_is_type (listv, XMMSV_TYPE_LIST), 0);
01216 
01217     l = listv->value.list;
01218 
01219     /* prevent accessing after the last element */
01220     if (!absolutify_and_validate_pos (&pos, l->size, 0)) {
01221         return 0;
01222     }
01223 
01224     if (val) {
01225         *val = l->list[pos];
01226     }
01227 
01228     return 1;
01229 }
01230 
01231 /**
01232  * Set the element at the given position in the list #xmmsv_t.
01233  *
01234  * @param listv A #xmmsv_t containing a list.
01235  * @param pos The position in the list. If negative, start counting
01236  *            from the end (-1 is the last element, etc).
01237  * @param val The element to put at the given position in the list.
01238  * @return 1 upon success otherwise 0
01239  */
01240 int
01241 xmmsv_list_set (xmmsv_t *listv, int pos, xmmsv_t *val)
01242 {
01243     xmmsv_t *old_val;
01244     xmmsv_list_t *l;
01245 
01246     x_return_val_if_fail (listv, 0);
01247     x_return_val_if_fail (val, 0);
01248     x_return_val_if_fail (xmmsv_is_type (listv, XMMSV_TYPE_LIST), 0);
01249 
01250     l = listv->value.list;
01251 
01252     if (!absolutify_and_validate_pos (&pos, l->size, 0)) {
01253         return 0;
01254     }
01255 
01256     old_val = l->list[pos];
01257     l->list[pos] = xmmsv_ref (val);
01258     xmmsv_unref (old_val);
01259 
01260     return 1;
01261 }
01262 
01263 /**
01264  * Insert an element at the given position in the list #xmmsv_t.
01265  * The list will hold a reference to the element until it's removed.
01266  *
01267  * @param listv A #xmmsv_t containing a list.
01268  * @param pos The position in the list. If negative, start counting
01269  *            from the end (-1 is the last element, etc).
01270  * @param val The element to insert.
01271  * @return 1 upon success otherwise 0
01272  */
01273 int
01274 xmmsv_list_insert (xmmsv_t *listv, int pos, xmmsv_t *val)
01275 {
01276     x_return_val_if_fail (listv, 0);
01277     x_return_val_if_fail (xmmsv_is_type (listv, XMMSV_TYPE_LIST), 0);
01278     x_return_val_if_fail (val, 0);
01279 
01280     return _xmmsv_list_insert (listv->value.list, pos, val);
01281 }
01282 
01283 /**
01284  * Remove the element at the given position from the list #xmmsv_t.
01285  *
01286  * @param listv A #xmmsv_t containing a list.
01287  * @param pos The position in the list. If negative, start counting
01288  *            from the end (-1 is the last element, etc).
01289  * @return 1 upon success otherwise 0
01290  */
01291 int
01292 xmmsv_list_remove (xmmsv_t *listv, int pos)
01293 {
01294     x_return_val_if_fail (listv, 0);
01295     x_return_val_if_fail (xmmsv_is_type (listv, XMMSV_TYPE_LIST), 0);
01296 
01297     return _xmmsv_list_remove (listv->value.list, pos);
01298 }
01299 
01300 /**
01301  * Move the element from position #old to position #new.
01302  *
01303  * #xmmsv_list_iter_t's remain pointing at their element (which might or might
01304  * not be at a different position).
01305  *
01306  * @param listv A #xmmsv_t containing a list
01307  * @param old The original position in the list. If negative, start counting
01308  *            from the end (-1 is the last element, etc.)
01309  * @param new The new position in the list. If negative start counting from the
01310  *            end (-1 is the last element, etc.) For the sake of counting the
01311  *            element to be moved is still at its old position.
01312  * @return 1 upon success otherwise 0
01313  */
01314 int
01315 xmmsv_list_move (xmmsv_t *listv, int old_pos, int new_pos)
01316 {
01317     x_return_val_if_fail (listv, 0);
01318     x_return_val_if_fail (xmmsv_is_type (listv, XMMSV_TYPE_LIST), 0);
01319 
01320     return _xmmsv_list_move (listv->value.list, old_pos, new_pos);
01321 }
01322 
01323 /**
01324  * Append an element to the end of the list #xmmsv_t.
01325  * The list will hold a reference to the element until it's removed.
01326  *
01327  * @param listv A #xmmsv_t containing a list.
01328  * @param val The element to append.
01329  * @return 1 upon success otherwise 0
01330  */
01331 int
01332 xmmsv_list_append (xmmsv_t *listv, xmmsv_t *val)
01333 {
01334     x_return_val_if_fail (listv, 0);
01335     x_return_val_if_fail (xmmsv_is_type (listv, XMMSV_TYPE_LIST), 0);
01336     x_return_val_if_fail (val, 0);
01337 
01338     return _xmmsv_list_append (listv->value.list, val);
01339 }
01340 
01341 /**
01342  * Empty the list from all its elements.
01343  *
01344  * @param listv A #xmmsv_t containing a list.
01345  * @return 1 upon success otherwise 0
01346  */
01347 int
01348 xmmsv_list_clear (xmmsv_t *listv)
01349 {
01350     x_return_val_if_fail (listv, 0);
01351     x_return_val_if_fail (xmmsv_is_type (listv, XMMSV_TYPE_LIST), 0);
01352 
01353     _xmmsv_list_clear (listv->value.list);
01354 
01355     return 1;
01356 }
01357 
01358 /**
01359  * Apply a function to each element in the list, in sequential order.
01360  *
01361  * @param listv A #xmmsv_t containing a list.
01362  * @param function The function to apply to each element.
01363  * @param user_data User data passed to the foreach function.
01364  * @return 1 upon success otherwise 0
01365  */
01366 int
01367 xmmsv_list_foreach (xmmsv_t *listv, xmmsv_list_foreach_func func,
01368                     void* user_data)
01369 {
01370     xmmsv_list_iter_t *it;
01371     xmmsv_t *v;
01372 
01373     x_return_val_if_fail (listv, 0);
01374     x_return_val_if_fail (xmmsv_is_type (listv, XMMSV_TYPE_LIST), 0);
01375     x_return_val_if_fail (xmmsv_get_list_iter (listv, &it), 0);
01376 
01377     while (xmmsv_list_iter_valid (it)) {
01378         xmmsv_list_iter_entry (it, &v);
01379         func (v, user_data);
01380         xmmsv_list_iter_next (it);
01381     }
01382 
01383     xmmsv_list_iter_free (it);
01384 
01385     return 1;
01386 }
01387 
01388 /**
01389  * Return the size of the list.
01390  *
01391  * @param listv The #xmmsv_t containing the list.
01392  * @return The size of the list, or -1 if listv is invalid.
01393  */
01394 int
01395 xmmsv_list_get_size (xmmsv_t *listv)
01396 {
01397     x_return_val_if_fail (listv, -1);
01398     x_return_val_if_fail (xmmsv_is_type (listv, XMMSV_TYPE_LIST), -1);
01399 
01400     return listv->value.list->size;
01401 }
01402 
01403 
01404 int
01405 xmmsv_list_restrict_type (xmmsv_t *listv, xmmsv_type_t type)
01406 {
01407     xmmsv_list_iter_t *it;
01408     xmmsv_t *v;
01409 
01410     x_return_val_if_fail (listv, 0);
01411     x_return_val_if_fail (xmmsv_is_type (listv, XMMSV_TYPE_LIST), 0);
01412 
01413     x_return_val_if_fail (!listv->value.list->restricted, 0);
01414 
01415     x_return_val_if_fail (xmmsv_get_list_iter (listv, &it), 0);
01416     while (xmmsv_list_iter_valid (it)) {
01417         xmmsv_list_iter_entry (it, &v);
01418         x_return_val_if_fail (xmmsv_is_type (v, type), 0);
01419         xmmsv_list_iter_next (it);
01420     }
01421 
01422     xmmsv_list_iter_free (it);
01423 
01424     listv->value.list->restricted = true;
01425     listv->value.list->restricttype = type;
01426 
01427     return 1;
01428 }
01429 
01430 
01431 static xmmsv_list_iter_t *
01432 xmmsv_list_iter_new (xmmsv_list_t *l)
01433 {
01434     xmmsv_list_iter_t *it;
01435 
01436     it = x_new0 (xmmsv_list_iter_t, 1);
01437     if (!it) {
01438         x_oom ();
01439         return NULL;
01440     }
01441 
01442     it->parent = l;
01443     it->position = 0;
01444 
01445     /* register iterator into parent */
01446     l->iterators = x_list_prepend (l->iterators, it);
01447 
01448     return it;
01449 }
01450 
01451 static void
01452 xmmsv_list_iter_free (xmmsv_list_iter_t *it)
01453 {
01454     /* unref iterator from list and free it */
01455     it->parent->iterators = x_list_remove (it->parent->iterators, it);
01456     free (it);
01457 }
01458 
01459 /**
01460  * Explicitly free list iterator.
01461  *
01462  * Immediately frees any resources used by this iterator. The iterator
01463  * is freed automatically when the list is freed, but this function is
01464  * useful when the list can be long lived.
01465  *
01466  * @param it iterator to free
01467  *
01468  */
01469 void
01470 xmmsv_list_iter_explicit_destroy (xmmsv_list_iter_t *it)
01471 {
01472     xmmsv_list_iter_free (it);
01473 }
01474 
01475 /**
01476  * Get the element currently pointed at by the iterator. This function
01477  * does not increase the refcount of the element, the reference is
01478  * still owned by the list. If iterator does not point on a valid
01479  * element xmmsv_list_iter_entry returns 0 and leaves val untouched.
01480  *
01481  * @param it A #xmmsv_list_iter_t.
01482  * @param val Pointer set to a borrowed reference to the element
01483  *            pointed at by the iterator.
01484  * @return 1 upon success otherwise 0
01485  */
01486 int
01487 xmmsv_list_iter_entry (xmmsv_list_iter_t *it, xmmsv_t **val)
01488 {
01489     if (!xmmsv_list_iter_valid (it))
01490         return 0;
01491 
01492     *val = it->parent->list[it->position];
01493 
01494     return 1;
01495 }
01496 
01497 /**
01498  * Check whether the iterator is valid and points to a valid element.
01499  *
01500  * @param it A #xmmsv_list_iter_t.
01501  * @return 1 if the iterator is valid, 0 otherwise
01502  */
01503 int
01504 xmmsv_list_iter_valid (xmmsv_list_iter_t *it)
01505 {
01506     return it && (it->position < it->parent->size) && (it->position >= 0);
01507 }
01508 
01509 /**
01510  * Rewind the iterator to the start of the list.
01511  *
01512  * @param it A #xmmsv_list_iter_t.
01513  */
01514 void
01515 xmmsv_list_iter_first (xmmsv_list_iter_t *it)
01516 {
01517     x_return_if_fail (it);
01518 
01519     it->position = 0;
01520 }
01521 
01522 /**
01523  * Move the iterator to end of the list.
01524  *
01525  * @param listv A #xmmsv_list_iter_t.
01526  */
01527 void
01528 xmmsv_list_iter_last (xmmsv_list_iter_t *it)
01529 {
01530     x_return_if_fail (it);
01531 
01532     if (it->parent->size > 0) {
01533         it->position = it->parent->size - 1;
01534     } else {
01535         it->position = it->parent->size;
01536     }
01537 }
01538 
01539 /**
01540  * Advance the iterator to the next element in the list.
01541  *
01542  * @param it A #xmmsv_list_iter_t.
01543  */
01544 void
01545 xmmsv_list_iter_next (xmmsv_list_iter_t *it)
01546 {
01547     x_return_if_fail (it);
01548 
01549     if (it->position < it->parent->size) {
01550         it->position++;
01551     }
01552 }
01553 
01554 /**
01555  * Move the iterator to the previous element in the list.
01556  *
01557  * @param listv A #xmmsv_list_iter_t.
01558  */
01559 void
01560 xmmsv_list_iter_prev (xmmsv_list_iter_t *it)
01561 {
01562     x_return_if_fail (it);
01563 
01564     if (it->position >= 0) {
01565         it->position--;
01566     }
01567 }
01568 
01569 
01570 /**
01571  * Move the iterator to the n-th element in the list.
01572  *
01573  * @param it A #xmmsv_list_iter_t.
01574  * @param pos The position in the list. If negative, start counting
01575  *            from the end (-1 is the last element, etc).
01576  * @return 1 upon success otherwise 0
01577  */
01578 int
01579 xmmsv_list_iter_seek (xmmsv_list_iter_t *it, int pos)
01580 {
01581     x_return_val_if_fail (it, 0);
01582 
01583     if (!absolutify_and_validate_pos (&pos, it->parent->size, 1)) {
01584         return 0;
01585     }
01586     it->position = pos;
01587 
01588     return 1;
01589 }
01590 
01591 /**
01592  * Tell the position of the iterator.
01593  *
01594  * @param it A #xmmsv_list_iter_t.
01595  * @return The position of the iterator, or -1 if invalid.
01596  */
01597 int
01598 xmmsv_list_iter_tell (const xmmsv_list_iter_t *it)
01599 {
01600     x_return_val_if_fail (it, -1);
01601 
01602     return it->position;
01603 }
01604 
01605 /**
01606  * Return the parent #xmmsv_t of an iterator.
01607  *
01608  * @param it A #xmmsv_list_iter_t.
01609  * @return The parent #xmmsv_t of the iterator, or NULL if invalid.
01610  */
01611 xmmsv_t*
01612 xmmsv_list_iter_get_parent (const xmmsv_list_iter_t *it)
01613 {
01614     x_return_val_if_fail (it, NULL);
01615 
01616     return it->parent->parent_value;
01617 }
01618 
01619 /**
01620  * Insert an element in the list at the position pointed at by the
01621  * iterator.
01622  *
01623  * @param it A #xmmsv_list_iter_t.
01624  * @param val The element to insert.
01625  * @return 1 upon success otherwise 0
01626  */
01627 int
01628 xmmsv_list_iter_insert (xmmsv_list_iter_t *it, xmmsv_t *val)
01629 {
01630     x_return_val_if_fail (it, 0);
01631     x_return_val_if_fail (val, 0);
01632 
01633     return _xmmsv_list_insert (it->parent, it->position, val);
01634 }
01635 
01636 /**
01637  * Remove the element in the list at the position pointed at by the
01638  * iterator.
01639  *
01640  * @param it A #xmmsv_list_iter_t.
01641  * @return 1 upon success otherwise 0
01642  */
01643 int
01644 xmmsv_list_iter_remove (xmmsv_list_iter_t *it)
01645 {
01646     x_return_val_if_fail (it, 0);
01647 
01648     return _xmmsv_list_remove (it->parent, it->position);
01649 }
01650 
01651 /* Dict stuff */
01652 
01653 struct xmmsv_dict_St {
01654     /* dict implemented as a flat [key1, val1, key2, val2, ...] list */
01655     xmmsv_list_t *flatlist;
01656     x_list_t *iterators;
01657 };
01658 
01659 struct xmmsv_dict_iter_St {
01660     /* iterator of the contained flatlist */
01661     xmmsv_list_iter_t *lit;
01662     xmmsv_dict_t *parent;
01663 };
01664 
01665 static xmmsv_dict_t *
01666 xmmsv_dict_new (void)
01667 {
01668     xmmsv_dict_t *dict;
01669 
01670     dict = x_new0 (xmmsv_dict_t, 1);
01671     if (!dict) {
01672         x_oom ();
01673         return NULL;
01674     }
01675 
01676     dict->flatlist = xmmsv_list_new ();
01677 
01678     return dict;
01679 }
01680 
01681 static void
01682 xmmsv_dict_free (xmmsv_dict_t *dict)
01683 {
01684     xmmsv_dict_iter_t *it;
01685 
01686     /* free iterators */
01687     while (dict->iterators) {
01688         it = (xmmsv_dict_iter_t *) dict->iterators->data;
01689         xmmsv_dict_iter_free (it);
01690     }
01691 
01692     xmmsv_list_free (dict->flatlist);
01693 
01694     free (dict);
01695 }
01696 
01697 /**
01698  * Get the element corresponding to the given key in the dict #xmmsv_t
01699  * (if it exists).  This function does not increase the refcount of
01700  * the element, the reference is still owned by the dict.
01701  *
01702  * @param dictv A #xmmsv_t containing a dict.
01703  * @param key The key in the dict.
01704  * @param val Pointer set to a borrowed reference to the element
01705  *            corresponding to the given key in the dict.
01706  * @return 1 upon success otherwise 0
01707  */
01708 int
01709 xmmsv_dict_get (xmmsv_t *dictv, const char *key, xmmsv_t **val)
01710 {
01711     xmmsv_dict_iter_t *it;
01712     int ret = 1;
01713 
01714     x_return_val_if_fail (key, 0);
01715     x_return_val_if_fail (dictv, 0);
01716     x_return_val_if_fail (xmmsv_is_type (dictv, XMMSV_TYPE_DICT), 0);
01717     x_return_val_if_fail (xmmsv_get_dict_iter (dictv, &it), 0);
01718 
01719     if (!xmmsv_dict_iter_find (it, key)) {
01720         ret = 0;
01721     }
01722 
01723     /* If found, return value and success */
01724     if (ret && val) {
01725         xmmsv_dict_iter_pair (it, NULL, val);
01726     }
01727 
01728     xmmsv_dict_iter_free (it);
01729 
01730     return ret;
01731 }
01732 
01733 /**
01734  * Insert an element under the given key in the dict #xmmsv_t. If the
01735  * key already referenced an element, that element is unref'd and
01736  * replaced by the new one.
01737  *
01738  * @param dictv A #xmmsv_t containing a dict.
01739  * @param key The key in the dict.
01740  * @param val The new element to insert in the dict.
01741  * @return 1 upon success otherwise 0
01742  */
01743 int
01744 xmmsv_dict_set (xmmsv_t *dictv, const char *key, xmmsv_t *val)
01745 {
01746     xmmsv_dict_iter_t *it;
01747     int ret;
01748 
01749     x_return_val_if_fail (key, 0);
01750     x_return_val_if_fail (val, 0);
01751     x_return_val_if_fail (dictv, 0);
01752     x_return_val_if_fail (xmmsv_is_type (dictv, XMMSV_TYPE_DICT), 0);
01753     x_return_val_if_fail (xmmsv_get_dict_iter (dictv, &it), 0);
01754 
01755     /* if key already present, replace value */
01756     if (xmmsv_dict_iter_find (it, key)) {
01757         ret = xmmsv_dict_iter_set (it, val);
01758 
01759     /* else, insert a new key-value pair */
01760     } else {
01761         xmmsv_t *keyval;
01762 
01763         keyval = xmmsv_new_string (key);
01764 
01765         ret = xmmsv_list_iter_insert (it->lit, keyval);
01766         if (ret) {
01767             xmmsv_list_iter_next (it->lit);
01768             ret = xmmsv_list_iter_insert (it->lit, val);
01769             if (!ret) {
01770                 /* we added the key, but we couldn't add the value.
01771                  * we remove the key again to put the dictionary back
01772                  * in a consistent state.
01773                  */
01774                 it->lit->position--;
01775                 xmmsv_list_iter_remove (it->lit);
01776             }
01777         }
01778         xmmsv_unref (keyval);
01779     }
01780 
01781     xmmsv_dict_iter_free (it);
01782 
01783     return ret;
01784 }
01785 
01786 /**
01787  * Remove the element corresponding to a given key in the dict
01788  * #xmmsv_t (if it exists).
01789  *
01790  * @param dictv A #xmmsv_t containing a dict.
01791  * @param key The key in the dict.
01792  * @return 1 upon success otherwise 0
01793  */
01794 int
01795 xmmsv_dict_remove (xmmsv_t *dictv, const char *key)
01796 {
01797     xmmsv_dict_iter_t *it;
01798     int ret = 1;
01799 
01800     x_return_val_if_fail (key, 0);
01801     x_return_val_if_fail (dictv, 0);
01802     x_return_val_if_fail (xmmsv_is_type (dictv, XMMSV_TYPE_DICT), 0);
01803     x_return_val_if_fail (xmmsv_get_dict_iter (dictv, &it), 0);
01804 
01805     if (!xmmsv_dict_iter_find (it, key)) {
01806         ret = 0;
01807     } else {
01808         ret = xmmsv_list_iter_remove (it->lit) &&
01809               xmmsv_list_iter_remove (it->lit);
01810         /* FIXME: cleanup if only the first fails */
01811     }
01812 
01813     xmmsv_dict_iter_free (it);
01814 
01815     return ret;
01816 }
01817 
01818 /**
01819  * Empty the dict of all its elements.
01820  *
01821  * @param dictv A #xmmsv_t containing a dict.
01822  * @return 1 upon success otherwise 0
01823  */
01824 int
01825 xmmsv_dict_clear (xmmsv_t *dictv)
01826 {
01827     x_return_val_if_fail (dictv, 0);
01828     x_return_val_if_fail (xmmsv_is_type (dictv, XMMSV_TYPE_DICT), 0);
01829 
01830     _xmmsv_list_clear (dictv->value.dict->flatlist);
01831 
01832     return 1;
01833 }
01834 
01835 /**
01836  * Apply a function to each key-element pair in the list. No
01837  * particular order is assumed.
01838  *
01839  * @param dictv A #xmmsv_t containing a dict.
01840  * @param function The function to apply to each key-element pair.
01841  * @param user_data User data passed to the foreach function.
01842  * @return 1 upon success otherwise 0
01843  */
01844 int
01845 xmmsv_dict_foreach (xmmsv_t *dictv, xmmsv_dict_foreach_func func,
01846                     void *user_data)
01847 {
01848     xmmsv_dict_iter_t *it;
01849     const char *key;
01850     xmmsv_t *v;
01851 
01852     x_return_val_if_fail (dictv, 0);
01853     x_return_val_if_fail (xmmsv_is_type (dictv, XMMSV_TYPE_DICT), 0);
01854     x_return_val_if_fail (xmmsv_get_dict_iter (dictv, &it), 0);
01855 
01856     while (xmmsv_dict_iter_valid (it)) {
01857         xmmsv_dict_iter_pair (it, &key, &v);
01858         func (key, v, user_data);
01859         xmmsv_dict_iter_next (it);
01860     }
01861 
01862     xmmsv_dict_iter_free (it);
01863 
01864     return 1;
01865 }
01866 
01867 /**
01868  * Return the size of the dict.
01869  *
01870  * @param dictv The #xmmsv_t containing the dict.
01871  * @return The size of the dict, or -1 if dict is invalid.
01872  */
01873 int
01874 xmmsv_dict_get_size (xmmsv_t *dictv)
01875 {
01876     x_return_val_if_fail (dictv, -1);
01877     x_return_val_if_fail (xmmsv_is_type (dictv, XMMSV_TYPE_DICT), -1);
01878 
01879     return dictv->value.dict->flatlist->size / 2;
01880 }
01881 
01882 static xmmsv_dict_iter_t *
01883 xmmsv_dict_iter_new (xmmsv_dict_t *d)
01884 {
01885     xmmsv_dict_iter_t *it;
01886 
01887     it = x_new0 (xmmsv_dict_iter_t, 1);
01888     if (!it) {
01889         x_oom ();
01890         return NULL;
01891     }
01892 
01893     it->lit = xmmsv_list_iter_new (d->flatlist);
01894     it->parent = d;
01895 
01896     /* register iterator into parent */
01897     d->iterators = x_list_prepend (d->iterators, it);
01898 
01899     return it;
01900 }
01901 
01902 static void
01903 xmmsv_dict_iter_free (xmmsv_dict_iter_t *it)
01904 {
01905     /* we don't free the parent list iter, already managed by the flatlist */
01906 
01907     /* unref iterator from dict and free it */
01908     it->parent->iterators = x_list_remove (it->parent->iterators, it);
01909     free (it);
01910 }
01911 
01912 /**
01913  * Explicitly free dict iterator.
01914  *
01915  * Immediately frees any resources used by this iterator. The iterator
01916  * is freed automatically when the dict is freed, but this function is
01917  * useful when the dict can be long lived.
01918  *
01919  * @param it iterator to free
01920  *
01921  */
01922 void
01923 xmmsv_dict_iter_explicit_destroy (xmmsv_dict_iter_t *it)
01924 {
01925     xmmsv_dict_iter_free (it);
01926 }
01927 
01928 /**
01929  * Get the key-element pair currently pointed at by the iterator. This
01930  * function does not increase the refcount of the element, the
01931  * reference is still owned by the dict.
01932  *
01933  * @param it A #xmmsv_dict_iter_t.
01934  * @param key Pointer set to the key pointed at by the iterator.
01935  * @param val Pointer set to a borrowed reference to the element
01936  *            pointed at by the iterator.
01937  * @return 1 upon success otherwise 0
01938  */
01939 int
01940 xmmsv_dict_iter_pair (xmmsv_dict_iter_t *it, const char **key,
01941                       xmmsv_t **val)
01942 {
01943     unsigned int orig;
01944     xmmsv_t *v;
01945 
01946     if (!xmmsv_dict_iter_valid (it)) {
01947         return 0;
01948     }
01949 
01950     /* FIXME: avoid leaking abstraction! */
01951     orig = it->lit->position;
01952 
01953     if (key) {
01954         xmmsv_list_iter_entry (it->lit, &v);
01955         xmmsv_get_string (v, key);
01956     }
01957 
01958     if (val) {
01959         xmmsv_list_iter_next (it->lit);
01960         xmmsv_list_iter_entry (it->lit, val);
01961     }
01962 
01963     it->lit->position = orig;
01964 
01965     return 1;
01966 }
01967 
01968 /**
01969  * Check whether the iterator is valid and points to a valid pair.
01970  *
01971  * @param it A #xmmsv_dict_iter_t.
01972  * @return 1 if the iterator is valid, 0 otherwise
01973  */
01974 int
01975 xmmsv_dict_iter_valid (xmmsv_dict_iter_t *it)
01976 {
01977     return it && xmmsv_list_iter_valid (it->lit);
01978 }
01979 
01980 /**
01981  * Rewind the iterator to the start of the dict.
01982  *
01983  * @param it A #xmmsv_dict_iter_t.
01984  * @return 1 upon success otherwise 0
01985  */
01986 void
01987 xmmsv_dict_iter_first (xmmsv_dict_iter_t *it)
01988 {
01989     x_return_if_fail (it);
01990 
01991     xmmsv_list_iter_first (it->lit);
01992 }
01993 
01994 /**
01995  * Advance the iterator to the next pair in the dict.
01996  *
01997  * @param it A #xmmsv_dict_iter_t.
01998  * @return 1 upon success otherwise 0
01999  */
02000 void
02001 xmmsv_dict_iter_next (xmmsv_dict_iter_t *it)
02002 {
02003     x_return_if_fail (it);
02004 
02005     /* skip a pair */
02006     xmmsv_list_iter_next (it->lit);
02007     xmmsv_list_iter_next (it->lit);
02008 }
02009 
02010 /**
02011  * Move the iterator to the pair with the given key (if it exists)
02012  * or move it to the position where the key would have to be
02013  * put (if it doesn't exist yet).
02014  *
02015  * @param it A #xmmsv_dict_iter_t.
02016  * @param key The key to seek for.
02017  * @return 1 upon success otherwise 0
02018  */
02019 int
02020 xmmsv_dict_iter_find (xmmsv_dict_iter_t *it, const char *key)
02021 {
02022     xmmsv_t *val;
02023     const char *k;
02024     int s, dict_size, cmp, left, right;
02025 
02026     x_return_val_if_fail (it, 0);
02027     x_return_val_if_fail (key, 0);
02028 
02029     /* how many key-value pairs does this dictionary contain? */
02030     dict_size = it->parent->flatlist->size / 2;
02031 
02032     /* if it's empty, point the iterator at the beginning of
02033      * the list and report failure.
02034      */
02035     if (!dict_size) {
02036         xmmsv_list_iter_seek (it->lit, 0);
02037 
02038         return 0;
02039     }
02040 
02041     /* perform binary search for the given key */
02042     left = 0;
02043     right = dict_size - 1;
02044 
02045     while (left <= right) {
02046         int mid = left + ((right - left) / 2);
02047 
02048         /* jump to the middle of the current search area */
02049         xmmsv_list_iter_seek (it->lit, mid * 2);
02050         xmmsv_list_iter_entry (it->lit, &val);
02051 
02052         /* get the key at this slot */
02053         s = xmmsv_get_string (val, &k);
02054         x_return_val_if_fail (s, 0);
02055 
02056         /* and compare it to the given key */
02057         cmp = strcmp (k, key);
02058 
02059         /* hooray, we found the key. */
02060         if (cmp == 0)
02061             return 1;
02062 
02063         /* go on searching the left or the right hand side. */
02064         if (cmp < 0) {
02065             left = mid + 1;
02066         } else {
02067             right = mid - 1;
02068         }
02069     }
02070 
02071     /* if we get down here, we failed to find the key
02072      * in the dictionary.
02073      * now, move the iterator so that it points to the slot
02074      * where the key would be inserted.
02075      */
02076     if (cmp < 0) {
02077         xmmsv_list_iter_next (it->lit);
02078         xmmsv_list_iter_next (it->lit);
02079     }
02080 
02081     return 0;
02082 }
02083 
02084 /**
02085  * Replace the element of the pair currently pointed to by the
02086  * iterator.
02087  *
02088  * @param it A #xmmsv_dict_iter_t.
02089  * @param val The element to set in the pair.
02090  * @return 1 upon success otherwise 0
02091  */
02092 int
02093 xmmsv_dict_iter_set (xmmsv_dict_iter_t *it, xmmsv_t *val)
02094 {
02095     unsigned int orig;
02096     int ret;
02097 
02098     x_return_val_if_fail (xmmsv_dict_iter_valid (it), 0);
02099 
02100     /* FIXME: avoid leaking abstraction! */
02101     orig = it->lit->position;
02102 
02103     xmmsv_list_iter_next (it->lit);
02104     xmmsv_list_iter_remove (it->lit);
02105     ret = xmmsv_list_iter_insert (it->lit, val);
02106     /* FIXME: check remove success, swap operations? */
02107 
02108     it->lit->position = orig;
02109 
02110     return ret;
02111 }
02112 
02113 /**
02114  * Remove the pair in the dict pointed at by the iterator.
02115  *
02116  * @param it A #xmmsv_dict_iter_t.
02117  * @return 1 upon success otherwise 0
02118  */
02119 int
02120 xmmsv_dict_iter_remove (xmmsv_dict_iter_t *it)
02121 {
02122     int ret = 0;
02123 
02124     ret = xmmsv_list_iter_remove (it->lit) &&
02125           xmmsv_list_iter_remove (it->lit);
02126     /* FIXME: cleanup if only the first fails */
02127 
02128     return ret;
02129 }
02130 
02131 
02132 
02133 /**
02134  * Decode an URL-encoded string.
02135  *
02136  * Some strings (currently only the url of media) has no known
02137  * encoding, and must be encoded in an UTF-8 clean way. This is done
02138  * similar to the url encoding web browsers do. This functions decodes
02139  * a string encoded in that way. OBSERVE that the decoded string HAS
02140  * NO KNOWN ENCODING and you cannot display it on screen in a 100%
02141  * guaranteed correct way (a good heuristic is to try to validate the
02142  * decoded string as UTF-8, and if it validates assume that it is an
02143  * UTF-8 encoded string, and otherwise fall back to some other
02144  * encoding).
02145  *
02146  * Do not use this function if you don't understand the
02147  * implications. The best thing is not to try to display the url at
02148  * all.
02149  *
02150  * Note that the fact that the string has NO KNOWN ENCODING and CAN
02151  * NOT BE DISPLAYED does not stop you from open the file if it is a
02152  * local file (if it starts with "file://").
02153  *
02154  * @param url the #xmmsv_t containing a url-encoded string
02155  * @return a new #xmmsv_t containing the decoded string as a XMMSV_BIN or NULL on failure
02156  *
02157  */
02158 xmmsv_t *
02159 xmmsv_decode_url (const xmmsv_t *inv)
02160 {
02161     int i = 0, j = 0;
02162     const char *ins;
02163     unsigned char *url;
02164     xmmsv_t *ret;
02165 
02166     if (!xmmsv_get_string (inv, &ins)) {
02167         return NULL;
02168     }
02169 
02170     url = x_malloc (strlen (ins));
02171     if (!url) {
02172         x_oom ();
02173         return NULL;
02174     }
02175 
02176     while (ins[i]) {
02177         unsigned char chr = ins[i++];
02178 
02179         if (chr == '+') {
02180             chr = ' ';
02181         } else if (chr == '%') {
02182             char ts[3];
02183             char *t;
02184 
02185             ts[0] = ins[i++];
02186             if (!ts[0])
02187                 goto err;
02188             ts[1] = ins[i++];
02189             if (!ts[1])
02190                 goto err;
02191             ts[2] = '\0';
02192 
02193             chr = strtoul (ts, &t, 16);
02194 
02195             if (t != &ts[2])
02196                 goto err;
02197         }
02198 
02199         url[j++] = chr;
02200     }
02201 
02202     ret = xmmsv_new_bin (url, j);
02203     free (url);
02204 
02205     return ret;
02206 
02207 err:
02208     free (url);
02209     return NULL;
02210 }
02211 
02212 xmmsv_t *
02213 xmmsv_build_dict (const char *firstkey, ...)
02214 {
02215     va_list ap;
02216     const char *key;
02217     xmmsv_t *val, *res;
02218 
02219     res = xmmsv_new_dict ();
02220     if (!res)
02221         return NULL;
02222 
02223     va_start (ap, firstkey);
02224 
02225     key = firstkey;
02226     do {
02227         val = va_arg (ap, xmmsv_t *);
02228 
02229         if (!xmmsv_dict_set (res, key, val)) {
02230             xmmsv_unref (res);
02231             res = NULL;
02232             break;
02233         }
02234         xmmsv_unref (val);
02235         key = va_arg (ap, const char *);
02236     } while (key);
02237 
02238     va_end (ap);
02239 
02240     return res;
02241 }
02242 
02243 xmmsv_t *
02244 xmmsv_build_list_va (xmmsv_t *first_entry, va_list ap)
02245 {
02246     xmmsv_t *val, *res;
02247 
02248     res = xmmsv_new_list ();
02249     if (!res)
02250         return NULL;
02251 
02252     val = first_entry;
02253 
02254     while (val) {
02255         if (!xmmsv_list_append (res, val)) {
02256             xmmsv_unref (res);
02257             res = NULL;
02258             break;
02259         }
02260 
02261         xmmsv_unref (val);
02262 
02263         val = va_arg (ap, xmmsv_t *);
02264     }
02265 
02266     return res;
02267 }
02268 
02269 xmmsv_t *
02270 xmmsv_build_list (xmmsv_t *first_entry, ...)
02271 {
02272     va_list ap;
02273     xmmsv_t *res;
02274 
02275     va_start (ap, first_entry);
02276     res = xmmsv_build_list_va (first_entry, ap);
02277     va_end (ap);
02278 
02279     return res;
02280 }
02281 
02282 
02283 /**
02284  * This function will make a pretty string about the information in
02285  * xmmsv dict.
02286  *
02287  * @param target A allocated char *
02288  * @param len Length of target
02289  * @param fmt A format string to use. You can insert items from the dict by
02290  * using specialformat "${field}".
02291  * @param val The #xmmsv_t that contains the dict.
02292  *
02293  * @returns The number of chars written to target
02294  */
02295 int
02296 xmmsv_dict_format (char *target, int len, const char *fmt, xmmsv_t *val)
02297 {
02298     const char *pos;
02299 
02300     if (!target) {
02301         return 0;
02302     }
02303 
02304     if (!fmt) {
02305         return 0;
02306     }
02307 
02308     memset (target, 0, len);
02309 
02310     pos = fmt;
02311     while (strlen (target) + 1 < len) {
02312         char *next_key, *key, *end;
02313         int keylen;
02314         xmmsv_dict_iter_t *it;
02315         xmmsv_t *v;
02316 
02317         next_key = strstr (pos, "${");
02318         if (!next_key) {
02319             strncat (target, pos, len - strlen (target) - 1);
02320             break;
02321         }
02322 
02323         strncat (target, pos, MIN (next_key - pos, len - strlen (target) - 1));
02324         keylen = strcspn (next_key + 2, "}");
02325         key = malloc (keylen + 1);
02326 
02327         if (!key) {
02328             fprintf (stderr, "Unable to allocate %u bytes of memory, OOM?", keylen);
02329             break;
02330         }
02331 
02332         memset (key, 0, keylen + 1);
02333         strncpy (key, next_key + 2, keylen);
02334 
02335         xmmsv_get_dict_iter (val, &it);
02336 
02337         if (strcmp (key, "seconds") == 0) {
02338             int duration;
02339 
02340             if (xmmsv_dict_iter_find (it, "duration")) {
02341                 xmmsv_dict_iter_pair (it, NULL, &v);
02342                 xmmsv_get_int (v, &duration);
02343             } else {
02344                 duration = 0;
02345             }
02346 
02347             if (!duration) {
02348                 strncat (target, "00", len - strlen (target) - 1);
02349             } else {
02350                 char seconds[10];
02351                 /* rounding */
02352                 duration += 500;
02353                 snprintf (seconds, sizeof (seconds), "%02d", (duration/1000)%60);
02354                 strncat (target, seconds, len - strlen (target) - 1);
02355             }
02356         } else if (strcmp (key, "minutes") == 0) {
02357             int duration;
02358 
02359             if (xmmsv_dict_iter_find (it, "duration")) {
02360                 xmmsv_dict_iter_pair (it, NULL, &v);
02361                 xmmsv_get_int (v, &duration);
02362             } else {
02363                 duration = 0;
02364             }
02365 
02366             if (!duration) {
02367                 strncat (target, "00", len - strlen (target) - 1);
02368             } else {
02369                 char minutes[10];
02370                 /* rounding */
02371                 duration += 500;
02372                 snprintf (minutes, sizeof (minutes), "%02d", duration/60000);
02373                 strncat (target, minutes, len - strlen (target) - 1);
02374             }
02375         } else {
02376             const char *result = NULL;
02377             char tmp[12];
02378 
02379             if (xmmsv_dict_iter_find (it, key)) {
02380                 xmmsv_dict_iter_pair (it, NULL, &v);
02381 
02382                 xmmsv_type_t type = xmmsv_get_type (v);
02383                 if (type == XMMSV_TYPE_STRING) {
02384                     xmmsv_get_string (v, &result);
02385                 } else if (type == XMMSV_TYPE_UINT32) {
02386                     uint32_t ui;
02387                     xmmsv_get_uint (v, &ui);
02388                     snprintf (tmp, 12, "%u", ui);
02389                     result = tmp;
02390                 } else if (type == XMMSV_TYPE_INT32) {
02391                     int32_t i;
02392                     xmmsv_get_int (v, &i);
02393                     snprintf (tmp, 12, "%d", i);
02394                     result = tmp;
02395                 }
02396             }
02397 
02398             if (result)
02399                 strncat (target, result, len - strlen (target) - 1);
02400         }
02401 
02402         free (key);
02403         end = strchr (next_key, '}');
02404 
02405         if (!end) {
02406             break;
02407         }
02408 
02409         pos = end + 1;
02410     }
02411 
02412     return strlen (target);
02413 }
02414 
02415 static int
02416 _xmmsv_utf8_charlen (unsigned char c)
02417 {
02418     if ((c & 0x80) == 0) {
02419         return 1;
02420     } else if ((c & 0x60) == 0x40) {
02421         return 2;
02422     } else if ((c & 0x70) == 0x60) {
02423         return 3;
02424     } else if ((c & 0x78) == 0x70) {
02425         return 4;
02426     }
02427     return 0;
02428 }
02429 
02430 
02431 /**
02432  * Check if a string is valid UTF-8.
02433  *
02434  */
02435 int
02436 xmmsv_utf8_validate (const char *str)
02437 {
02438     int i = 0;
02439 
02440     for (;;) {
02441         unsigned char c = str[i++];
02442         int l;
02443         if (!c) {
02444             /* NUL - end of string */
02445             return 1;
02446         }
02447 
02448         l = _xmmsv_utf8_charlen (c);
02449         if (l == 0)
02450             return 0;
02451         while (l-- > 1) {
02452             if ((str[i++] & 0xC0) != 0x80)
02453                 return 0;
02454         }
02455     }
02456 }
02457 
02458 
02459 /** @} */
02460 
02461 
02462 /**
02463  * @internal
02464  */
02465 static int
02466 absolutify_and_validate_pos (int *pos, int size, int allow_append)
02467 {
02468     x_return_val_if_fail (size >= 0, 0);
02469 
02470     if (*pos < 0) {
02471         if (-*pos > size)
02472             return 0;
02473         *pos = size + *pos;
02474     }
02475 
02476     if (*pos > size)
02477         return 0;
02478 
02479     if (!allow_append && *pos == size)
02480         return 0;
02481 
02482     return 1;
02483 }
02484 
02485 int
02486 xmmsv_dict_has_key (xmmsv_t *dictv, const char *key)
02487 {
02488     return xmmsv_dict_get (dictv, key, NULL);
02489 }