pdict.c
Go to the documentation of this file.
00001 #include "../stdafx.h"
00002 #include <stdlib.h>
00003 #include <stdio.h>
00004 #include <string.h>
00005 #include <assert.h>
00006 #include <regex.h>
00007 #if defined(_WINDOWS) && !defined(_CYGWIN)
00008 #include "wincompat.h"
00009 #endif
00010 #include "utils.h"
00011 #include "plist.h"
00012 #include "ptree.h"
00013 #include "pdict-impl.h"
00014 
00015 typedef struct pdict_ent pdict_ent_t;
00016 typedef int (*pdict_walk_int_func_t)(pdict_ent_t *pde, void *arg);
00017 
00018 static int _pdict_ent_add_persistent_change_listeners(pdict_t *pd,
00019     pdict_ent_t *pde);
00020 static int _pdict_ent_add_persistent_change_listener(pdict_ent_t *pde,
00021     pdict_persistent_listener_t *pl);
00022 static int _pdict_ent_remove_persistent_change_listener(pdict_ent_t *pde,
00023     pdict_persistent_listener_t *pl);
00024 static void _pdict_ent_notify(pdict_ent_t *pde, int reason, const char *ov);
00025 static int _pdict_ent_add_change_listener(pdict_ent_t *pde,
00026     pdl_notify_func_t notify, void *arg);
00027 static int _pdict_walk_int(pdict_t *pd, pdict_walk_int_func_t w, void *arg);
00028 static int _pdict_ent_remove_change_listener(pdict_ent_t *pde,
00029     pdl_notify_func_t notify, void *a);
00030 
00031 pdict_t *
00032 pdict_alloc(void)
00033 {
00034         pdict_t *pd;
00035 
00036         if (!(pd = malloc(sizeof (*pd))))
00037                 return 0;
00038         memset(pd, 0, sizeof (*pd));
00039 
00040         return pd;
00041 }
00042 
00043 static int
00044 pdecmp(const void *sv, const void *dv)
00045 {
00046         return strcmp(*(char **)sv, *(char **)dv);
00047 }
00048 
00049 static int
00050 pdict_ent_remove_change_listeners_cb(const void *v, const void *v0, void *a)
00051 {
00052         free((void *)v); v = NULL;
00053         return (1);
00054 }
00055 
00056 static int
00057 pdict_ent_remove_change_listeners(pdict_ent_t *pde)
00058 {
00059         plist_walk(pde->pde_listeners, pdict_ent_remove_change_listeners_cb, 0);
00060         plist_clear(&pde->pde_listeners);
00061         return 1;
00062 }
00063 
00064 static int
00065 pdict_ent_listeners_copy_cb(const void *v, const void *v0, void *a)
00066 {
00067         pdict_listener_t *pdl = (pdict_listener_t *)v;
00068         pdict_ent_t *pde_ent_copy = a;
00069 
00070         return _pdict_ent_add_change_listener(pde_ent_copy, pdl->pdl_notify, pdl->pdl_arg);
00071 }
00072 
00073 static void
00074 _pdict_ent_listeners_copy(pdict_ent_t *pde, pdict_ent_t *pde_copy)
00075 {
00076         plist_walk(pde->pde_listeners, pdict_ent_listeners_copy_cb, pde_copy);
00077 }
00078 
00079 /*
00080  * Sets or resets an entry to the dictionary, returning 0 on failure.
00081  */
00082 int
00083 pdict_add(pdict_t *pd, const char *k, const char *v, const char **ovp)
00084 {
00085         pdict_ent_t *n;
00086         pdict_ent_t n_copy;
00087         const char *ov;
00088 
00089         if (!(k = strdup(k)))
00090                 return 0;
00091         if (!(v = strdup(v))) {
00092                 free((void *)k); k = NULL;
00093                 return 0;
00094         }
00095         memset(&n_copy, 0, sizeof(pdict_ent_t));
00096         if (ptree_contains((void *)&k, pd->pd_ents, pdecmp, (void *)&n)) {
00097                 free((void *)k); k = NULL;
00098                 ov = n->pde_val;
00099                 n->pde_val = v;
00100 
00101                 if (ovp)
00102                         *ovp = ov;
00103                 else {
00104                         free((void *)ov); ov = NULL;
00105                 }
00106                 
00107                 //We copy n so that it's safe if it gets removed during a callback
00108                 if(n->pde_listeners)
00109                 {
00110                         n_copy.pde_key = strdup(n->pde_key);
00111                         n_copy.pde_val = strdup(n->pde_val);
00112                         _pdict_ent_listeners_copy(n, &n_copy);
00113                         _pdict_ent_notify(&n_copy, PDR_VALUE_CHANGED, ov);
00114                         pdict_ent_remove_change_listeners(&n_copy);
00115                         free((char *)n_copy.pde_key);
00116                         free((char *)n_copy.pde_val);
00117                 }
00118                 return 1;
00119         }
00120         if (!(n = malloc(sizeof (*n)))) {
00121                 free((void *)k); k = NULL;
00122                 free((void *)v); v = NULL;
00123                 return (0);
00124         }
00125         memset(n, 0, sizeof (*n));
00126         n->pde_key = k;
00127         n->pde_val = v;
00128 
00129         //Add dict listeners to the dict entry object
00130         if (!_pdict_ent_add_persistent_change_listeners(pd, n)) {
00131                 free((void *)k); k = NULL;
00132                 free((void *)v); v = NULL;
00133                 free(n); n = NULL;
00134                 return (0);
00135         }
00136         //Add the dict entry to the dict (replacing if there is a matching key).
00137         if (!ptree_replace(n, &pd->pd_ents, pdecmp, NULL)) {
00138                 pdict_ent_remove_change_listeners(n);
00139                 free((void *)k); k = NULL;
00140                 free((void *)v); v = NULL;
00141                 free(n); n = NULL;
00142                 return (0);
00143         }
00144         //notify the listeners
00145         if(n->pde_listeners)
00146         {
00147                 n_copy.pde_key = strdup(n->pde_key);
00148                 n_copy.pde_val = strdup(n->pde_val);
00149                 _pdict_ent_listeners_copy(n, &n_copy);
00150                 _pdict_ent_notify(&n_copy, PDR_ENTRY_ADDED, n_copy.pde_val);
00151                 pdict_ent_remove_change_listeners(&n_copy);
00152                 free((char *)n_copy.pde_key);
00153                 free((char *)n_copy.pde_val);
00154         }
00155 
00156         if (ovp)
00157                 *ovp = NULL;
00158 
00159         return 1;
00160 }
00161 
00162 int
00163 pdict_ent_remove(pdict_t *pd, const char *k, char **ovp)
00164 {
00165         pdict_ent_t *n;
00166 
00167         pu_log(PUL_VERB, 0, "Removing in key pdict_ent_remove: %s", k);
00168         
00169         if (!ptree_remove((void *)&k, &pd->pd_ents, pdecmp, (void *)&n))
00170         {
00171                 //pu_log(PUL_INFO, 0, "Failed to remove key in pdict_ent_remove: %s", k);
00172                 return 0;
00173         }
00174         
00175         _pdict_ent_notify(n, PDR_ENTRY_REMOVING, n->pde_val);
00176         
00177         if (ovp)
00178                 *ovp = (char *)n->pde_val;
00179         else {
00180                 free((void *)n->pde_val);
00181         }
00182         free((void *)n->pde_key);
00183         pdict_ent_remove_change_listeners(n);
00184         free(n);
00185         return 1;
00186 }
00187 
00188 static int
00189 pdict_ent_remove_persistent_change_listener_cb(const void *pl, const void *v,
00190     void *a)
00191 {
00192         _pdict_ent_remove_persistent_change_listener((pdict_ent_t *)a,
00193             (pdict_persistent_listener_t *)pl);
00194         return 1;
00195 }
00196 
00197 static int
00198 pdict_ent_add_persistent_change_listener_cb(const void *lid, const void *pl,
00199     void *a)
00200 {
00201         return _pdict_ent_add_persistent_change_listener((pdict_ent_t *)a,
00202             (pdict_persistent_listener_t *)pl);
00203 }
00204 
00205 static int
00206 _pdict_ent_add_persistent_change_listeners(pdict_t *pd, pdict_ent_t *pde)
00207 {
00208         if (!plist_walk(pd->pd_persistent_listeners, pdict_ent_add_persistent_change_listener_cb, pde)) {
00209                 plist_walk(pd->pd_persistent_listeners, pdict_ent_remove_persistent_change_listener_cb, pde);
00210                 pu_log(PUL_WARN, 0, "Failed to add persistent change listener in _pdict_ent_add_persistent_change_listeners.");
00211                 return 0;
00212         }
00213         return 1;
00214 }
00215 
00216 static int
00217 pdict_ent_remove_persistent_change_listener_dcb(pdict_ent_t *pde, void *pl)
00218 {
00219         return _pdict_ent_remove_persistent_change_listener(pde,
00220             (pdict_persistent_listener_t *)pl);
00221 }
00222 
00223 static int
00224 pdict_ent_add_persistent_change_listener_dcb(pdict_ent_t *pde, void *pl)
00225 {
00226         return _pdict_ent_add_persistent_change_listener(pde,
00227             (pdict_persistent_listener_t *)pl);
00228 }
00229 
00230 static int
00231 _pdict_ent_add_persistent_change_listener(pdict_ent_t *pde,
00232     pdict_persistent_listener_t *pdpl)
00233 {
00234         int res;
00235 
00236         if ((res = regexec(&pdpl->pdpl_regex, pde->pde_key, 0, NULL, 0)) != 0)
00237         {
00238                 return res == REG_NOMATCH;
00239         }
00240         if (!_pdict_ent_add_change_listener(pde, pdpl->pdpl_l.pdl_notify, pdpl->pdpl_l.pdl_arg))
00241         {
00242                 pu_log(PUL_WARN, 0, "Failed to add persistent change listener in _pdict_ent_add_persistent_change_listener.");
00243                 return 0;
00244         }
00245         if (pdpl->pdpl_new)
00246                 pdpl->pdpl_l.pdl_notify(pde->pde_key, pde->pde_val, PDR_CURRENT_VALUE, NULL, pdpl->pdpl_l.pdl_arg);
00247         return 1;
00248 }
00249 
00250 static int
00251 _pdict_ent_remove_persistent_change_listener(pdict_ent_t *pde,
00252     pdict_persistent_listener_t *pl)
00253 {
00254         _pdict_ent_remove_change_listener(pde, pl->pdpl_l.pdl_notify,
00255             pl->pdpl_l.pdl_arg);
00256         return 1;
00257 }
00258 
00259 int
00260 pdict_remove_persistent_change_listener(pdict_t *pd, int id)
00261 {
00262         pdict_persistent_listener_t *pdpl;
00263 
00264         if (!plist_remove((void *)(size_t)id, &pd->pd_persistent_listeners, (void **)&pdpl) || !pdpl)
00265         {
00266                 pu_log(PUL_WARN, 0, "Failed plist_remove in pdict_remove_persistent_change_listener.");
00267                 return 0;
00268         }
00269         if (!_pdict_walk_int(pd, pdict_ent_remove_persistent_change_listener_dcb, pdpl))
00270         {
00271                 pu_log(PUL_WARN, 0, "Failed _pdict_walk_int in pdict_remove_persistent_change_listener.");
00272                 return 0;
00273         }
00274         regfree(&pdpl->pdpl_regex);
00275         free(pdpl); pdpl = NULL;
00276         return 1;
00277 }
00278 
00279 int
00280 pdict_add_persistent_change_listener(pdict_t *pd, const char *kpat,
00281     pdl_notify_func_t notify, void *arg)
00282 {
00283         pdict_persistent_listener_t *pl;
00284         static int lid = 1;
00285 
00286         if (!(pl = malloc(sizeof (*pl))))
00287                 return 0;
00288         memset(pl, 0, sizeof (*pl));
00289         pl->pdpl_l.pdl_notify = notify;
00290         pl->pdpl_l.pdl_arg = arg;
00291         if (regcomp(&pl->pdpl_regex, kpat, REG_EXTENDED | REG_NOSUB) != 0) {
00292                 // XXX todo: communicate error context is not libc
00293                 free(pl); pl = NULL;
00294                 pu_log(PUL_WARN, 0, "Failed regcomp in pdict_add_persistent_change_listener.");
00295                 return 0;
00296         }
00297 
00298         plist_add((void *)(size_t)lid, pl, &pd->pd_persistent_listeners);
00299 
00300         pl->pdpl_new = 1;
00301         if (!_pdict_walk_int(pd,
00302             pdict_ent_add_persistent_change_listener_dcb, pl)) {
00303                 _pdict_walk_int(pd,
00304                     pdict_ent_remove_persistent_change_listener_dcb, pl);
00305                 plist_remove((void *)(size_t)lid, &pd->pd_persistent_listeners, NULL);
00306                 regfree(&pl->pdpl_regex);
00307                 free(pl); pl = NULL;
00308                 pu_log(PUL_WARN, 0, "Failed _pdict_walk_int in pdict_add_persistent_change_listener.");
00309                 return 0;
00310         }
00311         pl->pdpl_new = 0;
00312         return lid++;
00313 }
00314 
00315 /*
00316  * Return whether a given key is in the dictionary.  If the given
00317  * entry pointer is non-NULL, set it to the entry.
00318  */
00319 static int
00320 _pdict_ent_lookup(pdict_t *pd, const char *k, pdict_ent_t **e)
00321 {
00322         return ptree_contains((void *)&k, pd->pd_ents, pdecmp, (void **)e);
00323 }
00324 
00325 int
00326 pdict_ent_lookup(pdict_t *pd, const char *k, const char **v)
00327 {
00328         pdict_ent_t *pde;
00329 
00330         if (_pdict_ent_lookup(pd, k, &pde)) {
00331                 if (v)
00332                         *v = strdup(pde->pde_val);
00333                 return 1;
00334         }
00335         return 0;
00336 }
00337 
00338 static int
00339 pdict_ent_remove_change_listener_cb(const void *k, const void *v, void *a)
00340 {
00341         pdict_listener_t *l = (pdict_listener_t *)k;
00342         void **arg = (void **)a;
00343 
00344         if (l->pdl_notify == (pdl_notify_func_t)arg[0] && l->pdl_arg == arg[1]) {
00345                 arg[2] = l;
00346                 return 0;
00347         }
00348         return 1;
00349 }
00350 
00351 static int
00352 _pdict_ent_remove_change_listener(pdict_ent_t *pde, pdl_notify_func_t notify,
00353     void *a)
00354 {
00355         void *arg[3];
00356 
00357         arg[0] = (void *)notify;
00358         arg[1] = a;
00359         arg[2] = NULL;
00360         plist_walk(pde->pde_listeners, pdict_ent_remove_change_listener_cb, arg);
00361         if (arg[2]) {
00362                 plist_remove(arg[2], &pde->pde_listeners, NULL);
00363                 free(arg[2]); arg[2] = NULL;
00364                 return 1;
00365         }
00366         return 0;
00367 }
00368 
00369 static int
00370 _pdict_ent_add_change_listener(pdict_ent_t *pde, pdl_notify_func_t notify,
00371     void *arg)
00372 {
00373         pdict_listener_t *l;
00374 
00375         if (!(l = malloc(sizeof (*l))))
00376                 return 0;
00377         memset(l, 0, sizeof (*l));
00378         l->pdl_notify = notify;
00379         l->pdl_arg = arg;
00380         if (!plist_add(l, 0, &pde->pde_listeners)) {
00381                 free(l); l = NULL;
00382                 pu_log(PUL_WARN, 0, "Failed plist_add in _pdict_ent_add_change_listener.");
00383                 return 0;
00384         }
00385 
00386         return 1;
00387 }
00388 
00389 int
00390 pdict_ent_add_change_listener(pdict_t *pd, const char *k,
00391     pdl_notify_func_t notify, void *arg)
00392 {
00393         pdict_ent_t *pde;
00394 
00395         if (!_pdict_ent_lookup(pd, k, &pde))
00396                 return 0;
00397         return _pdict_ent_add_change_listener(pde, notify, arg);
00398 }
00399 
00400 typedef struct {
00401         pdict_ent_t *penca_pde;
00402         pdict_reason_t penca_reason;
00403         const char *penca_ov;
00404 } pdict_ent_notify_cb_args_t;
00405 
00406 static int
00407 pdict_ent_notify_cb(const void *v, const void *v0, void *a)
00408 {
00409         pdict_listener_t *pdl = (pdict_listener_t *)v;
00410         pdict_ent_notify_cb_args_t *penca = a;
00411 
00412         pdl->pdl_notify(penca->penca_pde->pde_key, penca->penca_pde->pde_val,
00413             penca->penca_reason, penca->penca_ov, pdl->pdl_arg);
00414         return 1;
00415 }
00416 
00417 static void
00418 _pdict_ent_notify(pdict_ent_t *pde, int reason, const char *ov)
00419 {
00420         pdict_ent_notify_cb_args_t penca = { pde, reason, ov };
00421 
00422         plist_walk(pde->pde_listeners, pdict_ent_notify_cb, &penca);
00423 }
00424 
00425 static ptree_walk_res_t
00426 pdict_walk_int_cb(const void *v, int level, void *a, void *pwra)
00427 {
00428         void **args = a;
00429 
00430         return ((pdict_walk_int_func_t)args[0])((pdict_ent_t *)v, args[1]);
00431 }
00432 
00433 static ptree_walk_res_t
00434 pdict_walk_cb(const void *v, int level, void *a, void *pwra)
00435 {
00436         pdict_ent_t *pde = (pdict_ent_t *)v;
00437         void **args = a;
00438 
00439         return ((pdict_walk_func_t)args[0])(pde->pde_key, pde->pde_val,
00440             args[1]);
00441 }
00442 
00443 static int
00444 _pdict_walk_int(pdict_t *pd, pdict_walk_int_func_t w, void *arg)
00445 {
00446         void *args[2] = { (void *)w, arg };
00447 
00448         return ptree_walk(pd->pd_ents, PTREE_INORDER, pdict_walk_int_cb, pdecmp, args);
00449 }
00450 
00451 int
00452 pdict_walk(pdict_t *pd, pdict_walk_func_t w, void *arg)
00453 {
00454         void *args[2] = { (void *)w, arg };
00455 
00456         return ptree_walk(pd->pd_ents, PTREE_INORDER, pdict_walk_cb, NULL, args);
00457 }
00458 
00459 int
00460 pdict_ent_remove_change_listener(pdict_t *pd, const char *k,
00461     pdl_notify_func_t nf, void *arg)
00462 {
00463         pdict_ent_t *e;
00464         int res;
00465 
00466         if (!(res = _pdict_ent_lookup(pd, k, &e)))
00467                 return 0;
00468         return _pdict_ent_remove_change_listener(e, nf, arg);
00469 }
00470 
00471 const char *
00472 pdict_reason_str(pdict_reason_t r)
00473 {
00474         switch (r) {
00475         case PDR_VALUE_CHANGED:
00476                 return "changed";
00477         case PDR_ENTRY_ADDED:
00478                 return "added";
00479         case PDR_ENTRY_REMOVING:
00480                 return "removing";
00481         case PDR_CURRENT_VALUE:
00482                 return "current";
00483         default:
00484                 return "?";
00485         }
00486 }
00487 
00488 pdict_reason_t
00489 pdict_reason_from_str(const char *s)
00490 {
00491         if (strcmp(s, "changed") == 0)
00492                 return PDR_VALUE_CHANGED;
00493         if (strcmp(s, "current") == 0)
00494                 return PDR_CURRENT_VALUE;
00495         if (strcmp(s, "added") == 0)
00496                 return PDR_ENTRY_ADDED;
00497         if (strcmp(s, "removing") == 0)
00498                 return PDR_ENTRY_REMOVING;
00499         return 0;
00500 }


pedal_monitor
Author(s): Pedro Mendes
autogenerated on Fri Jun 6 2014 18:37:21