The Assimilation Monitoring Project
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
configcontext.c
Go to the documentation of this file.
1 
23 #include <configcontext.h>
24 #include <memory.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdio.h>
28 
29 #define BROKEN_G_SLIST_FREE_FULL 1
30 #undef BROKEN_G_SLIST_FREE_FULL
31 
32 #ifdef BROKEN_G_SLIST_FREE_FULL
33 # undef g_slist_free_full
34 # define g_slist_free_full assim_slist_free_full
35 #endif
36 
38 FSTATIC enum ConfigValType _configcontext_gettype(const ConfigContext*, const char *name);
39 FSTATIC ConfigValue* _configcontext_getvalue(const ConfigContext*, const char *name);
42 FSTATIC gint64 _configcontext_getint(const ConfigContext*, const char *name);
43 FSTATIC void _configcontext_setint(ConfigContext*, const char *name, gint value);
44 FSTATIC gboolean _configcontext_getbool(const ConfigContext*, const char *name);
45 FSTATIC void _configcontext_setbool(ConfigContext*, const char *name, gboolean value);
46 FSTATIC const char* _configcontext_getstring(const ConfigContext*, const char *name);
47 FSTATIC void _configcontext_setstring(ConfigContext*, const char *name, const char *value);
48 FSTATIC GSList* _configcontext_getarray(const ConfigContext*, const char *name);
49 FSTATIC void _configcontext_setarray(ConfigContext*, const char *name, GSList*value);
51 FSTATIC void _configcontext_setaddr(ConfigContext*, const char *name, NetAddr*);
52 FSTATIC Frame* _configcontext_getframe(const ConfigContext*, const char *name);
53 FSTATIC void _configcontext_setframe(ConfigContext*, const char *name, Frame*);
55  _configcontext_getconfig(const ConfigContext*, const char*);
56 FSTATIC char* _configcontext_getstr(const ConfigContext*, const char*);
58 FSTATIC gint _configcontext_key_compare(gconstpointer a, gconstpointer b);
59 
60 
61 
62 FSTATIC char * _configcontext_toString(gconstpointer aself);
64 FSTATIC char * JSONquotestring(char * s);
72 FSTATIC gboolean _configcontext_JSON_parse_array(GScanner* scan, GSList** retval);
74 FSTATIC void _configcontext_value_vfinalize(gpointer vself);
76 FSTATIC void _key_free(gpointer vself);
77 #ifdef BROKEN_G_SLIST_FREE_FULL
78 void assim_slist_free_full(GSList* list, void (*)(gpointer));
79 #endif
80 
89 #ifdef BROKEN_G_SLIST_FREE_FULL
90 void
91 assim_slist_free_full(GSList* list, void (*datafree)(gpointer))
92 {
93  GSList* this = NULL;
94  GSList* next = NULL;
95  //fprintf(stderr, "Freeing GSList at %p\n", list);
96 
97  for (this=list; this; this=next) {
98  next=this->next;
99  //fprintf(stderr, "........Freeing GSList data at %p\n", this->data);
100  if (this->data) {
101  datafree(this->data);
102  }else{
103  fprintf(stderr, "........NO GSList data (NULL) at %p\n"
104  , this->data);
105  }
106  //fprintf(stderr, "........Freeing GSList element at %p\n", this);
107  memset(this, 0, sizeof(*this));
108  g_slist_free_1(this);
109  }
110 }
111 #endif
112 
113 FSTATIC void
114 _key_free(gpointer vself)
115 {
116  //g_message("Freeing key pointer at %p\n", vself);
117  g_free(vself);
118 }
119 
120 
123 configcontext_new(gsize objsize)
124 {
125  AssimObj * baseobj = NULL;
126  ConfigContext * newcontext = NULL;
127 
128  if (objsize < sizeof(ConfigContext)) {
129  objsize = sizeof(ConfigContext);
130  }
131  baseobj = assimobj_new(objsize);
132  newcontext = NEWSUBCLASS(ConfigContext, baseobj);
133  newcontext->setint = _configcontext_setint;
134  newcontext->getint = _configcontext_getint;
135  newcontext->getbool = _configcontext_getbool;
136  newcontext->setbool = _configcontext_setbool;
137  newcontext->setstring = _configcontext_setstring;
138  newcontext->getstring = _configcontext_getstring;
139  newcontext->getframe = _configcontext_getframe;
140  newcontext->setframe = _configcontext_setframe;
141  newcontext->getaddr = _configcontext_getaddr;
142  newcontext->setaddr = _configcontext_setaddr;
143  newcontext->setconfig = _configcontext_setconfig;
144  newcontext->getconfig = _configcontext_getconfig;
145  newcontext->setarray = _configcontext_setarray;
146  newcontext->getarray = _configcontext_getarray;
147  newcontext->gettype = _configcontext_gettype;
148  newcontext->getvalue = _configcontext_getvalue;
149  newcontext->keys = _configcontext_keys;
150  newcontext->keycount = _configcontext_keycount;
151  newcontext->_values = g_hash_table_new_full(g_str_hash, g_str_equal, _key_free
155  return newcontext;
156 }
157 
159 FSTATIC void
161 {
162  ConfigContext* self = CASTTOCLASS(ConfigContext, aself);
163 
164  if (self->_values) {
165  g_hash_table_destroy(self->_values);
166  self->_values = NULL;
167  }
168  FREECLASSOBJ(self);
169 }
170 
172 FSTATIC gint
173 _configcontext_key_compare(gconstpointer a, gconstpointer b)
174 {
175  return strcmp((const char *)a, (const char*)b);
176 }
177 
179 FSTATIC guint
181 {
182  GHashTableIter iter;
183  gpointer key;
184  gpointer data;
185  guint ret = 0;
186 
187  g_hash_table_iter_init(&iter, cfg->_values);
188  while (g_hash_table_iter_next(&iter, &key, &data)) {
189  ++ret;
190  }
191  return ret;
192 }
193 
195 FSTATIC GSList*
197 {
198  GSList* keylist = NULL;
199  GHashTableIter iter;
200  gpointer key;
201  gpointer data;
202 
203  if (!cfg->_values) {
204  return NULL;
205  }
206 
207  g_hash_table_iter_init(&iter, cfg->_values);
208  while (g_hash_table_iter_next(&iter, &key, &data)) {
209  keylist = g_slist_prepend(keylist, key);
210  }
211  keylist= g_slist_sort(keylist, _configcontext_key_compare);
212  return keylist;
213 }
214 
217 _configcontext_gettype(const ConfigContext* self, const char *name)
218 {
219  gpointer ret = g_hash_table_lookup(self->_values, name);
220  ConfigValue* cfg;
221  if (ret == NULL) {
222  return CFG_EEXIST;
223  }
224  cfg = CASTTOCLASS(ConfigValue, ret);
225  return cfg->valtype;
226 }
227 
230 _configcontext_getvalue(const ConfigContext* self, const char *name)
231 {
232  gpointer ret = g_hash_table_lookup(self->_values, name);
233  if (ret != NULL) {
234  return CASTTOCLASS(ConfigValue, ret);
235  }
236  return NULL;
237 }
238 
240 FSTATIC gint64
242  , const char *name)
243 {
244  gpointer ret = g_hash_table_lookup(self->_values, name);
245  ConfigValue* cfg;
246 
247  if (ret == NULL) {
248  return -1;
249  }
250  cfg = CASTTOCLASS(ConfigValue, ret);
251  if (cfg->valtype != CFG_INT64) {
252  return -1;
253  }
254 
255  return cfg->u.intvalue;
256 }
257 
259 FSTATIC void
261  , const char *name
262  , gint value)
263 {
265  char * cpname = g_strdup(name);
266 
267  val->u.intvalue = value;
268  g_hash_table_replace(self->_values, cpname, val);
269 }
270 
272 FSTATIC gboolean
274  , const char *name)
275 {
276  gpointer ret = g_hash_table_lookup(self->_values, name);
277  ConfigValue* cfg;
278 
279  if (ret == NULL) {
280  return -1;
281  }
282  cfg = CASTTOCLASS(ConfigValue, ret);
283  if (cfg->valtype != CFG_BOOL) {
284  return -1;
285  }
286 
287  return (gboolean)cfg->u.intvalue;
288 }
289 
291 FSTATIC void
293  , const char *name
294  , gint value)
295 {
297  char * cpname = g_strdup(name);
298 
299  val->u.intvalue = value;
300  g_hash_table_replace(self->_values, cpname, val);
301 }
302 
304 FSTATIC const char*
306  , const char *name)
307 {
308  gpointer ret = g_hash_table_lookup(self->_values, name);
309  ConfigValue* cfg;
310 
311  if (ret == NULL) {
312  return NULL;
313  }
314  cfg = CASTTOCLASS(ConfigValue, ret);
315  if (cfg->valtype != CFG_STRING) {
316  return NULL;
317  }
318  return cfg->u.strvalue;
319 }
320 
322 FSTATIC void
324  ,const char *name
325  ,const char *value)
326 {
328 
329  val->u.strvalue = g_strdup(value);
330  g_hash_table_replace(self->_values, g_strdup(name), val);
331 }
332 FSTATIC GSList*
333 _configcontext_getarray(const ConfigContext* self, const char *name)
334 {
335  gpointer ret = g_hash_table_lookup(self->_values, name);
336  ConfigValue* cfg;
337 
338  if (ret == NULL) {
339  return NULL;
340  }
341  cfg = CASTTOCLASS(ConfigValue, ret);
342  if (cfg->valtype != CFG_ARRAY) {
343  g_warning("getarray called on object of type %d", cfg->valtype);
344  return NULL;
345  }
346  //g_warning("getarray[%s] on %s gives %p", name, self->baseclass.toString(self), cfg->u.arrayvalue);
347  return cfg->u.arrayvalue;
348 }
349 FSTATIC void
350 _configcontext_setarray(ConfigContext*self, const char *name, GSList*value)
351 {
352  char * cpname = g_strdup(name);
354  val->u.arrayvalue = value;
355 
357  g_hash_table_replace(self->_values, cpname, val);
358 }
359 
360 
364  , const char *name)
365 {
366  gpointer ret = g_hash_table_lookup(self->_values, name);
367  ConfigValue* cfg;
368 
369  if (ret == NULL) {
370  return NULL;
371  }
372  cfg = CASTTOCLASS(ConfigValue, ret);
373  if (cfg->valtype != CFG_NETADDR) {
374  return NULL;
375  }
376  return cfg->u.addrvalue;
377 }
378 
380 FSTATIC void
382  , const char * name
383  , NetAddr* addr)
384 {
385  char * cpname = g_strdup(name);
387 
388  REF(addr);
389  val->u.addrvalue = addr;
390  g_hash_table_replace(self->_values, cpname, val);
391 }
392 
394 FSTATIC Frame*
396  , const char *name)
397 {
398  gpointer ret = g_hash_table_lookup(self->_values, name);
399  ConfigValue* cfg;
400 
401  if (ret == NULL) {
402  return NULL;
403  }
404  cfg = CASTTOCLASS(ConfigValue, ret);
405  if (cfg->valtype != CFG_FRAME) {
406  return NULL;
407  }
408  return cfg->u.framevalue;
409 }
410 
412 FSTATIC void
414  , const char * name
415  , Frame* frame)
416 
417 {
418  char * cpname = g_strdup(name);
420 
421  REF(frame);
422  val->u.framevalue = frame;
423  g_hash_table_replace(self->_values, cpname, val);
424 }
425 
428 _configcontext_getconfig(const ConfigContext* self , const char* name)
429 {
430  gpointer ret = g_hash_table_lookup(self->_values, name);
431  ConfigValue* cfg;
432 
433  if (ret == NULL) {
434  return NULL;
435  }
436  cfg = CASTTOCLASS(ConfigValue, ret);
437  if (cfg->valtype != CFG_CFGCTX) {
438  return NULL;
439  }
440  return cfg->u.cfgctxvalue;
441 }
443 FSTATIC void
444 _configcontext_setconfig(ConfigContext* self,const char *name, ConfigContext* value)
445 {
446  char * cpname = g_strdup(name);
448 
449  REF(value);
450  val->u.cfgctxvalue = value;
451  g_hash_table_replace(self->_values, cpname, val);
452 }
453 
455 FSTATIC char*
456 _configcontext_getstr(const ConfigContext* self , const char* name)
457 {
458  ConfigValue* cfval = self->getvalue(self, name);
459  if (cfval == NULL) {
460  return NULL;
461  }
462  return _configcontext_elem_toString(cfval);
463 }
464 
468 {
469  AssimObj* aret;
470  ConfigValue* ret;
471 
472  aret = assimobj_new(sizeof(ConfigValue));
473  ret = NEWSUBCLASS(ConfigValue, aret);
474  ret->valtype = t;
475  memset(&ret->u, 0, sizeof(ret->u));
477  return ret;
478 }
479 
481 FSTATIC void
483 {
484  ConfigValue* self;
485  //fprintf(stderr, "configcontext_value_vfinalize(%p)\n", vself);
486  self = CASTTOCLASS(ConfigValue, vself);
487  UNREF(self);
488  vself = NULL;
489 }
490 FSTATIC void
492 {
493  ConfigValue* self;
494 
495  //fprintf(stderr, "configcontext_value_finalize(%p)\n", aself);
496  self = CASTTOCLASS(ConfigValue, aself);
497  //fprintf(stderr, "configcontext_value_finalize(%p): %d\n"
498  //, aself, self->valtype);
499  switch (self->valtype) {
500  case CFG_STRING:
501  g_free(self->u.strvalue); self->u.strvalue = NULL;
502  break;
503  case CFG_CFGCTX: {
504  UNREF(self->u.cfgctxvalue);
505  break;
506  }
507  case CFG_NETADDR: {
508  UNREF(self->u.addrvalue);
509  break;
510  }
511  case CFG_FRAME: {
512  UNREF(self->u.framevalue);
513  break;
514  }
515  case CFG_ARRAY: {
516  GSList* list = self->u.arrayvalue;
518  self->u.arrayvalue = NULL;
519  break;
520  }
521 
522  default: {
523  // Do nothing
524  break;
525  }
526  }
527  self->valtype = CFG_EEXIST;
528  memset(self, 0, sizeof(*self));
529  FREECLASSOBJ(self);
530  self = NULL;
531  aself = NULL;
532 }
533 
534 
535 #define JSONQUOTES "\\\""
536 
537 FSTATIC char *
539 {
540  GString* ret;
541  char * str;
542  ret = g_string_sized_new(strlen(s)+5);
543  g_string_append_c(ret, '"');
544 
545  for (str=s; *str; ++str ) {
546  if (strchr(JSONQUOTES, *str )) {
547  g_string_append_c(ret, '\\');
548  }
549  g_string_append_c(ret, *str);
550  }
551  g_string_append_c(ret, '"');
552  return g_string_free(ret, FALSE);
553 }
554 
555 
557 FSTATIC char *
558 _configcontext_toString(gconstpointer aself)
559 {
560  const ConfigContext* self = CASTTOCONSTCLASS(ConfigContext, aself);
561 
562  GString* gsret = g_string_new("{");
563  GSList* keyelem;
564  GSList* nextkeyelem;
565  const char * comma = "";
566 
567  if (!self->_values) {
568  return NULL;
569  }
570  for (keyelem = self->keys(self); keyelem; keyelem = nextkeyelem) {
571  char * thiskey = keyelem->data;
572  ConfigValue* val = self->getvalue(self, thiskey);
573  gchar* elem = _configcontext_elem_toString(val);
574  g_string_append_printf(gsret, "%s\"%s\":%s", comma, thiskey, elem);
575  g_free(elem);
576  comma=",";
577  nextkeyelem = keyelem->next;
578  g_slist_free1(keyelem);
579  keyelem = NULL;
580  }
581  g_string_append(gsret, "}");
582  return g_string_free(gsret, FALSE);
583 }
585 FSTATIC char *
587 {
588  switch (val->valtype) {
589  case CFG_BOOL:
590  return g_strdup(val->u.intvalue? "true" : "false");
591 
592  case CFG_INT64:
593  return g_strdup_printf(FMT_64BIT"d", val->u.intvalue);
594 
595  case CFG_FLOAT:
596  return g_strdup_printf("%g", val->u.floatvalue);
597 
598  case CFG_STRING: {
599  //g_message("Got string pointer: %p", val->u.strvalue);
600  //g_message("Got string: %s", val->u.strvalue);
601  return JSONquotestring(val->u.strvalue);
602  }
603 
604  case CFG_CFGCTX: {
605  return val->u.cfgctxvalue->baseclass.toString(val->u.cfgctxvalue);
606  }
607  case CFG_ARRAY: {
608  const char * acomma = "";
609  GString* ret = g_string_new("[");
610  GSList* this;
611 
612  for (this = val->u.arrayvalue; this; this = this->next) {
613  ConfigValue* val = CASTTOCLASS(ConfigValue, this->data);
614  gchar* elem = _configcontext_elem_toString(val);
615  g_string_append_printf(ret, "%s%s", acomma, elem);
616  g_free(elem);
617  acomma=",";
618  }
619  g_string_append(ret, "]");
620  return g_string_free(ret, FALSE);
621  }
622  case CFG_NETADDR: {
623 
624 
625  AssimObj* obj = CASTTOCLASS(AssimObj, val->u.addrvalue);
626  char* tostring = obj->toString(obj);
627  gchar* retstr = JSONquotestring(tostring);
628  g_free(tostring); tostring = NULL;
629  return retstr;
630  }
631  case CFG_FRAME: {
632  AssimObj* obj = CASTTOCLASS(AssimObj, val->u.framevalue);
633  char* tostring = obj->toString(obj);
634  gchar* retstr = JSONquotestring(tostring);
635 
636  FREE(tostring); tostring=NULL;
637  return retstr;
638  }
639  case CFG_EEXIST:
640  case CFG_NULL:
641  return g_strdup("null");
642 
643  }//endswitch
644  /*NOTREACHED*/
645  return g_strdup("null");
646 }
647 
652 FSTATIC GScanner*
654 {
655  static GScannerConfig config;
656  GScanner* retval;
657  // Legal JSON keywords are true, false, and null
658  // There are no 'identifiers' as such.
659  static char firstchars[] = "tfn";
660  static char subsequentchars[] = "aelrsu";
661  static char whitespace[] = " \t\n\r\f";
662  static char True[] = "true";
663  static char False[] = "false";
664  static char Null[] = "null";
665  memset(&config, 0, sizeof(config));
666 
667  // For more info on what these settings do, see
668  // http://developer.gnome.org/glib/2.32/glib-Lexical-Scanner.html
669 
670  config.cset_skip_characters = whitespace;
671  config.cset_identifier_first = firstchars;
672  config.cset_identifier_nth = subsequentchars;
673  config.case_sensitive = TRUE;
674  config.skip_comment_multi = FALSE;
675  config.skip_comment_single = FALSE;
676  config.scan_comment_multi = FALSE;
677  config.scan_identifier = TRUE;
678  config.scan_identifier_1char = FALSE;
679  config.scan_identifier_NULL = FALSE;
680  config.scan_symbols = TRUE; // ???
681  config.scan_binary = FALSE;
682  config.scan_octal = FALSE;
683  config.scan_float = TRUE;
684  config.scan_hex = FALSE;
685  config.scan_hex_dollar = FALSE;
686  config.scan_string_sq = FALSE;
687  config.scan_string_dq = TRUE;
688  config.numbers_2_int = TRUE;
689  config.int_2_float = FALSE;
690  config.identifier_2_string = FALSE;
691  config.char_2_token = TRUE;
692  config.symbol_2_token = FALSE; // ???
693  config.scope_0_fallback = TRUE;
694  config.store_int64 = TRUE;
695 
696  retval = g_scanner_new(&config);
697  if (retval) {
698  g_scanner_scope_add_symbol(retval, 0, True, True);
699  g_scanner_scope_add_symbol(retval, 0, False, False);
700  g_scanner_scope_add_symbol(retval, 0, Null, Null);
701  }
702  return retval;
703 }
704 
705 #define TOKEN_COLON ':'
706 #define GULP (void)g_scanner_get_next_token(scan)
707 
708 #define SYNERROR(scan, token, symbol, msg) \
709  {g_warning("In Function %s line %d", __FUNCTION__, __LINE__);g_scanner_unexp_token(scan, token, "keyword", "keyword", symbol, msg, TRUE);}
710 
713 configcontext_new_JSON_string(const char * jsontext)
714 {
715  GScanner* scanner = _configcontext_JSON_GScanner_new();
716  ConfigContext* ret;
717 
718  g_scanner_input_text(scanner, jsontext, strlen(jsontext));
720  g_scanner_destroy(scanner);
721  return ret;
722 }
723 
727 {
729 
730  if (ret != NULL && g_scanner_get_next_token(scan) != G_TOKEN_EOF) {
731  SYNERROR(scan, G_TOKEN_EOF, NULL, NULL);
732  UNREF(ret);
733  }
734  return ret;
735 }
736 
740 {
741  ConfigContext* ret;
742  ConfigContext* membersret;
743  if (g_scanner_peek_next_token(scan) != G_TOKEN_LEFT_CURLY) {
744  GULP;
745  SYNERROR(scan, G_TOKEN_LEFT_CURLY, NULL, NULL);
746  return NULL;
747  }
748  GULP; // Swallow '{'
749  ret = configcontext_new(0);
750  if (g_scanner_peek_next_token(scan) == G_TOKEN_RIGHT_CURLY) {
751  // Empty 'object' - which is just fine...
752  GULP;
753  return ret;
754  }
755 
756  membersret = _configcontext_JSON_parse_members(scan, ret);
757  if (membersret == NULL) {
758  UNREF(ret);
759  return NULL;
760  }
761 
762  if (g_scanner_get_next_token(scan) != G_TOKEN_RIGHT_CURLY) {
763  // Syntax error...
764  SYNERROR(scan, G_TOKEN_RIGHT_CURLY, NULL, NULL);
765  UNREF(ret);
766  return NULL;
767  }
768  return ret;
769 }
773 {
774  while (g_scanner_peek_next_token(scan) == G_TOKEN_STRING) {
776  if (g_scanner_peek_next_token(scan) == G_TOKEN_COMMA) {
777  GULP;
778  }else{
779  break;
780  }
781  }
782  return cfg;
783 }
784 
785 // Parse a JSON "name": value pair
788 {
789  char * name = NULL;
790  ConfigValue* value;
791  // "name" : _value_ pairs
792  //
793  // Name is always a string -
794  // Value can be any of:
795  // string
796  // number
797  // object
798  // array
799  // true (a symbol)
800  // false (a symbol)
801  // null (a symbol)
802  if (g_scanner_peek_next_token(scan) != G_TOKEN_STRING) {
803  return NULL;
804  }
805  GULP;
806  // Get value of G_TOKEN_STRING
807  name = g_strdup(scan->value.v_string);
808  if (g_scanner_peek_next_token(scan) != TOKEN_COLON) {
809  SYNERROR(scan, TOKEN_COLON, NULL, NULL);
810  // Syntax error
811  g_free(name); name = NULL;
812  return NULL;
813  }
814  GULP; // Swallow TOKEN_COLON
815  if (g_scanner_peek_next_token(scan) == TOKEN_COLON) {
816  return NULL;
817  }
818  // Next is a value...
819  value = _configcontext_JSON_parse_value(scan);
820  if (value == NULL) {
821  // Syntax error - already noted by the lower layers...
822  g_free(name); name = NULL;
823  return NULL;
824  }
825  g_hash_table_replace(cfg->_values, name, value);
826  return cfg;
827 }
828 
831 {
832  guint toktype = g_scanner_peek_next_token(scan);
833  switch(toktype) {
834  case G_TOKEN_STRING:{ // String
836  ConfigValue* val;
837  NetAddr* encoded;
838  GULP;
839  // See if we can convert it to a NetAddr...
841  if ((encoded = netaddr_string_new(scan->value.v_string)) != NULL) {
843  val->u.addrvalue = encoded;
844  encoded = NULL;
845  }else{
847  val->u.strvalue = g_strdup(scan->value.v_string);
848  }
849  return val;
850  }
851 
852  case G_TOKEN_INT: { // Integer
854  GULP;
855  val->u.intvalue = scan->value.v_int64;
856  return val;
857  }
858  break;
859 
860  case G_TOKEN_FLOAT: { // Double value
862  GULP;
863  val->u.floatvalue = scan->value.v_float;
864  return val;
865  }
866  break;
867 
868  case G_TOKEN_SYMBOL: { // true, false, null
869  GULP;
870  if (strcmp(scan->value.v_string, "true") == 0 || strcmp(scan->value.v_string, "false") == 0) {
872  val->u.intvalue = (strcmp(scan->value.v_string, "true") == 0);
873  return val;
874  }else if (strcmp(scan->value.v_string, "null") == 0) {
876  }else{
877  SYNERROR(scan, G_TOKEN_NONE, NULL, "- expecting JSON value");
878  // Syntax error
879  return NULL;
880  }
881  }
882  break;
883 
884 
885  case G_TOKEN_LEFT_CURLY:{ // Object
886  ConfigValue* val;
887  ConfigContext* child;
888  child = _configcontext_JSON_parse_object(scan);
889  if (child == NULL) {
890  // Syntax error - detected by child object
891  return NULL;
892  }
894  val->u.cfgctxvalue = child;
895  return val;
896  }
897  break;
898 
899 
900  case G_TOKEN_LEFT_BRACE: { // Array
901  ConfigValue* val;
902  GSList* child = NULL;
903  if (!_configcontext_JSON_parse_array(scan, &child)) {
904  // Syntax error - detected by child object
905  return NULL;
906  }
908  val->u.arrayvalue = child;
909  return val;
910  }
911  break;
912 
913  // Things we don't support...
914  default:
915  // Syntax error
916  g_warning("Got token type %u", g_scanner_get_next_token(scan));
917  //GULP;
918  SYNERROR(scan, G_TOKEN_NONE, NULL, "Unexpected symbol.");
919  return NULL;
920  }
921  /*NOTREACHED*/
922  g_warning("Got token type %u", g_scanner_get_next_token(scan));
923  SYNERROR(scan, G_TOKEN_NONE, NULL, "Unexpected symbol.");
924  return NULL;
925 }
926 FSTATIC gboolean
927 _configcontext_JSON_parse_array(GScanner* scan, GSList** retval)
928 {
929  *retval = NULL;
930  if (g_scanner_peek_next_token(scan) != G_TOKEN_LEFT_BRACE) {
931  GULP;
932  SYNERROR(scan, G_TOKEN_LEFT_BRACE, NULL, NULL);
933  // Syntax error
934  return FALSE;
935  }
936  GULP; // Swallow left square bracket (G_TOKEN_LEFT_BRACE)
937  while (g_scanner_peek_next_token(scan) != G_TOKEN_RIGHT_BRACE
938  && !g_scanner_eof(scan)) {
939  ConfigValue * value;
940 
941  // Parse the value
942  value = _configcontext_JSON_parse_value(scan);
943  if (value == NULL) {
944  if (*retval != NULL) {
946  *retval = NULL;
947  return FALSE;
948  }
949  }else{
950  *retval = g_slist_append(*retval, value);
951  }
952  // Expect a comma
953  if (g_scanner_peek_next_token(scan) == G_TOKEN_COMMA) {
954  GULP;
955  }else if (g_scanner_peek_next_token(scan) != G_TOKEN_RIGHT_BRACE) {
956  SYNERROR(scan, G_TOKEN_RIGHT_BRACE, NULL, NULL);
957  GULP;
958  return FALSE;
959  }
960  }
961  if (g_scanner_peek_next_token(scan) == G_TOKEN_RIGHT_BRACE) {
962  GULP;
963  return TRUE;
964  }
965  SYNERROR(scan, G_TOKEN_RIGHT_BRACE, NULL, NULL);
966  return FALSE;
967 }