• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

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

Generated on Wed Feb 9 2011 for XMMS2 by  doxygen 1.7.1