The Assimilation 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 
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);
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 
363  , const char *name)
364 {
365  gpointer ret = g_hash_table_lookup(self->_values, name);
366  ConfigValue* cfg;
367 
368  if (ret == NULL) {
369  return NULL;
370  }
371  cfg = CASTTOCLASS(ConfigValue, ret);
372  if (cfg->valtype != CFG_NETADDR) {
373  return NULL;
374  }
375  return cfg->u.addrvalue;
376 }
377 
379 FSTATIC void
381  , const char * name
382  , NetAddr* addr)
383 {
384  char * cpname = g_strdup(name);
386 
387  REF(addr);
388  val->u.addrvalue = addr;
389  g_hash_table_replace(self->_values, cpname, val);
390 }
391 
393 FSTATIC Frame*
395  , const char *name)
396 {
397  gpointer ret = g_hash_table_lookup(self->_values, name);
398  ConfigValue* cfg;
399 
400  if (ret == NULL) {
401  return NULL;
402  }
403  cfg = CASTTOCLASS(ConfigValue, ret);
404  if (cfg->valtype != CFG_FRAME) {
405  return NULL;
406  }
407  return cfg->u.framevalue;
408 }
409 
411 FSTATIC void
413  , const char * name
414  , Frame* frame)
415 {
417  char * cpname = g_strdup(name);
419 
420  REF(frame);
421  val->u.framevalue = frame;
422  g_hash_table_replace(self->_values, cpname, val);
423 }
424 
427 _configcontext_getconfig(const ConfigContext* self , const char* name)
428 {
429  gpointer ret = g_hash_table_lookup(self->_values, name);
430  ConfigValue* cfg;
431 
432  if (ret == NULL) {
433  return NULL;
434  }
435  cfg = CASTTOCLASS(ConfigValue, ret);
436  if (cfg->valtype != CFG_CFGCTX) {
437  return NULL;
438  }
439  return cfg->u.cfgctxvalue;
440 }
442 FSTATIC void
444 {
445  char * cpname = g_strdup(name);
447 
448  REF(value);
449  val->u.cfgctxvalue = value;
450  g_hash_table_replace(self->_values, cpname, val);
451 }
452 
454 FSTATIC char*
455 _configcontext_getstr(const ConfigContext* self , const char* name)
456 {
457  ConfigValue* cfval = self->getvalue(self, name);
458  if (cfval == NULL) {
459  return NULL;
460  }
461  return _configcontext_elem_toString(cfval);
462 }
463 
467 {
468  AssimObj* aret;
469  ConfigValue* ret;
470 
471  aret = assimobj_new(sizeof(ConfigValue));
472  ret = NEWSUBCLASS(ConfigValue, aret);
473  ret->valtype = t;
474  memset(&ret->u, 0, sizeof(ret->u));
476  return ret;
477 }
478 
480 FSTATIC void
482 {
483  ConfigValue* self;
484  //fprintf(stderr, "configcontext_value_vfinalize(%p)\n", vself);
485  self = CASTTOCLASS(ConfigValue, vself);
486  UNREF(self);
487  vself = NULL;
488 }
489 FSTATIC void
491 {
492  ConfigValue* self;
493 
494  //fprintf(stderr, "configcontext_value_finalize(%p)\n", aself);
495  self = CASTTOCLASS(ConfigValue, aself);
496  //fprintf(stderr, "configcontext_value_finalize(%p): %d\n"
497  //, aself, self->valtype);
498  switch (self->valtype) {
499  case CFG_STRING:
500  g_free(self->u.strvalue); self->u.strvalue = NULL;
501  break;
502  case CFG_CFGCTX: {
503  UNREF(self->u.cfgctxvalue);
504  break;
505  }
506  case CFG_NETADDR: {
507  UNREF(self->u.addrvalue);
508  break;
509  }
510  case CFG_FRAME: {
511  UNREF(self->u.framevalue);
512  break;
513  }
514  case CFG_ARRAY: {
515  GSList* list = self->u.arrayvalue;
517  self->u.arrayvalue = NULL;
518  break;
519  }
520 
521  default: {
522  // Do nothing
523  break;
524  }
525  }
526  self->valtype = CFG_EEXIST;
527  memset(self, 0, sizeof(*self));
528  FREECLASSOBJ(self);
529  self = NULL;
530  aself = NULL;
531 }
532 
533 
534 #define JSONREPLACES "\\\"\b\f\n\r\t"
535 #define JSONREPLACEMENTS "\\\"bfnrt"
536 FSTATIC char *
539 {
540  GString* ret;
541  char * str;
542  const char * replacechars = JSONREPLACES;
543  ret = g_string_sized_new(strlen(s)+5);
544  g_string_append_c(ret, '"');
545 
546 
547  for (str=s; *str; ++str ) {
548  const char * found;
549  if (NULL != (found=strchr(replacechars, *str ))) {
550  size_t offset = found-replacechars;
551  g_string_append_c(ret, '\\');
552  g_string_append_c(ret, JSONREPLACEMENTS[offset]);
553  }else{
554  g_string_append_c(ret, *str);
555  }
556  }
557  g_string_append_c(ret, '"');
558  return g_string_free(ret, FALSE);
559 }
560 
562 FSTATIC char *
563 _configcontext_toString(gconstpointer aself)
564 {
565  const ConfigContext* self = CASTTOCONSTCLASS(ConfigContext, aself);
566 
567  GString* gsret = g_string_new("{");
568  GSList* keyelem;
569  GSList* nextkeyelem;
570  const char * comma = "";
571 
572  if (!self->_values) {
573  return NULL;
574  }
575  for (keyelem = self->keys(self); keyelem; keyelem = nextkeyelem) {
576  char * thiskey = keyelem->data;
577  ConfigValue* val = self->getvalue(self, thiskey);
578  gchar* elem = _configcontext_elem_toString(val);
579  g_string_append_printf(gsret, "%s\"%s\":%s", comma, thiskey, elem);
580  g_free(elem);
581  comma=",";
582  nextkeyelem = keyelem->next;
583  g_slist_free1(keyelem);
584  keyelem = NULL;
585  }
586  g_string_append(gsret, "}");
587  return g_string_free(gsret, FALSE);
588 }
590 FSTATIC char *
592 {
593  switch (val->valtype) {
594  case CFG_BOOL:
595  return g_strdup(val->u.intvalue? "true" : "false");
596 
597  case CFG_INT64:
598  return g_strdup_printf(FMT_64BIT"d", val->u.intvalue);
599 
600  case CFG_FLOAT:
601  return g_strdup_printf("%g", val->u.floatvalue);
602 
603  case CFG_STRING: {
604  //g_message("Got string pointer: %p", val->u.strvalue);
605  //g_message("Got string: %s", val->u.strvalue);
606  return JSONquotestring(val->u.strvalue);
607  }
608 
609  case CFG_CFGCTX: {
610  return val->u.cfgctxvalue->baseclass.toString(val->u.cfgctxvalue);
611  }
612  case CFG_ARRAY: {
613  const char * acomma = "";
614  GString* ret = g_string_new("[");
615  GSList* this;
616 
617  for (this = val->u.arrayvalue; this; this = this->next) {
618  ConfigValue* val = CASTTOCLASS(ConfigValue, this->data);
619  gchar* elem = _configcontext_elem_toString(val);
620  g_string_append_printf(ret, "%s%s", acomma, elem);
621  g_free(elem);
622  acomma=",";
623  }
624  g_string_append(ret, "]");
625  return g_string_free(ret, FALSE);
626  }
627  case CFG_NETADDR: {
628  AssimObj* obj = CASTTOCLASS(AssimObj, val->u.addrvalue);
631  char* tostring = obj->toString(obj);
632  gchar* retstr = JSONquotestring(tostring);
633  g_free(tostring); tostring = NULL;
634  return retstr;
635  }
636  case CFG_FRAME: {
637  AssimObj* obj = CASTTOCLASS(AssimObj, val->u.framevalue);
638  char* tostring = obj->toString(obj);
639  gchar* retstr = JSONquotestring(tostring);
640 
641  FREE(tostring); tostring=NULL;
642  return retstr;
643  }
644  case CFG_EEXIST:
645  case CFG_NULL:
646  return g_strdup("null");
647 
648  }//endswitch
649  /*NOTREACHED*/
650  return g_strdup("null");
651 }
652 
657 FSTATIC GScanner*
659 {
660  static GScannerConfig config;
661  GScanner* retval;
662  // Legal JSON keywords are true, false, and null
663  // There are no 'identifiers' as such.
664  static char firstchars[] = "tfn";
665  static char subsequentchars[] = "aelrsu";
666  static char whitespace[] = " \t\n\r\f";
667  static char True[] = "true";
668  static char False[] = "false";
669  static char Null[] = "null";
670  memset(&config, 0, sizeof(config));
671 
672  // For more info on what these settings do, see
673  // http://developer.gnome.org/glib/2.32/glib-Lexical-Scanner.html
674 
675  config.cset_skip_characters = whitespace;
676  config.cset_identifier_first = firstchars;
677  config.cset_identifier_nth = subsequentchars;
678  config.case_sensitive = TRUE;
679  config.skip_comment_multi = FALSE;
680  config.skip_comment_single = FALSE;
681  config.scan_comment_multi = FALSE;
682  config.scan_identifier = TRUE;
683  config.scan_identifier_1char = FALSE;
684  config.scan_identifier_NULL = FALSE;
685  config.scan_symbols = TRUE; // ???
686  config.scan_binary = FALSE;
687  config.scan_octal = FALSE;
688  config.scan_float = TRUE;
689  config.scan_hex = FALSE;
690  config.scan_hex_dollar = FALSE;
691  config.scan_string_sq = FALSE;
692  config.scan_string_dq = TRUE;
693  config.numbers_2_int = TRUE;
694  config.int_2_float = FALSE;
695  config.identifier_2_string = FALSE;
696  config.char_2_token = TRUE;
697  config.symbol_2_token = FALSE; // ???
698  config.scope_0_fallback = TRUE;
699  config.store_int64 = TRUE;
700 
701  retval = g_scanner_new(&config);
702  if (retval) {
703  g_scanner_scope_add_symbol(retval, 0, True, True);
704  g_scanner_scope_add_symbol(retval, 0, False, False);
705  g_scanner_scope_add_symbol(retval, 0, Null, Null);
706  }
707  return retval;
708 }
709 
710 #define TOKEN_COLON ':'
711 #define GULP (void)g_scanner_get_next_token(scan)
712 
713 #define SYNERROR(scan, token, symbol, msg) \
714  {g_warning("In Function %s line %d", __FUNCTION__, __LINE__);g_scanner_unexp_token(scan, token, "keyword", "keyword", symbol, msg, TRUE);}
715 
718 configcontext_new_JSON_string(const char * jsontext)
719 {
720  GScanner* scanner = _configcontext_JSON_GScanner_new();
721  ConfigContext* ret;
722 
723  g_scanner_input_text(scanner, jsontext, strlen(jsontext));
725  g_scanner_destroy(scanner);
726  return ret;
727 }
728 
732 {
734 
735  if (ret != NULL && g_scanner_get_next_token(scan) != G_TOKEN_EOF) {
736  SYNERROR(scan, G_TOKEN_EOF, NULL, NULL);
737  UNREF(ret);
738  }
739  return ret;
740 }
741 
745 {
746  ConfigContext* ret;
747  ConfigContext* membersret;
748  if (g_scanner_peek_next_token(scan) != G_TOKEN_LEFT_CURLY) {
749  GULP;
750  SYNERROR(scan, G_TOKEN_LEFT_CURLY, NULL, NULL);
751  return NULL;
752  }
753  GULP; // Swallow '{'
754  ret = configcontext_new(0);
755  if (g_scanner_peek_next_token(scan) == G_TOKEN_RIGHT_CURLY) {
756  // Empty 'object' - which is just fine...
757  GULP;
758  return ret;
759  }
760 
761  membersret = _configcontext_JSON_parse_members(scan, ret);
762  if (membersret == NULL) {
763  UNREF(ret);
764  return NULL;
765  }
766 
767  if (g_scanner_get_next_token(scan) != G_TOKEN_RIGHT_CURLY) {
768  // Syntax error...
769  SYNERROR(scan, G_TOKEN_RIGHT_CURLY, NULL, NULL);
770  UNREF(ret);
771  return NULL;
772  }
773  return ret;
774 }
778 {
779  while (g_scanner_peek_next_token(scan) == G_TOKEN_STRING) {
781  if (g_scanner_peek_next_token(scan) == G_TOKEN_COMMA) {
782  GULP;
783  }else{
784  break;
785  }
786  }
787  return cfg;
788 }
789 
790 // Parse a JSON "name": value pair
793 {
794  char * name = NULL;
795  ConfigValue* value;
796  // "name" : _value_ pairs
797  //
798  // Name is always a string -
799  // Value can be any of:
800  // string
801  // number
802  // object
803  // array
804  // true (a symbol)
805  // false (a symbol)
806  // null (a symbol)
807  if (g_scanner_peek_next_token(scan) != G_TOKEN_STRING) {
808  return NULL;
809  }
810  GULP;
811  // Get value of G_TOKEN_STRING
812  name = g_strdup(scan->value.v_string);
813  if (g_scanner_peek_next_token(scan) != TOKEN_COLON) {
814  SYNERROR(scan, TOKEN_COLON, NULL, NULL);
815  // Syntax error
816  g_free(name); name = NULL;
817  return NULL;
818  }
819  GULP; // Swallow TOKEN_COLON
820  if (g_scanner_peek_next_token(scan) == TOKEN_COLON) {
821  return NULL;
822  }
823  // Next is a value...
824  value = _configcontext_JSON_parse_value(scan);
825  if (value == NULL) {
826  // Syntax error - already noted by the lower layers...
827  g_free(name); name = NULL;
828  return NULL;
829  }
830  g_hash_table_replace(cfg->_values, name, value);
831  return cfg;
832 }
833 
836 {
837  guint toktype = g_scanner_peek_next_token(scan);
838  switch(toktype) {
839  case G_TOKEN_STRING:{ // String
841  ConfigValue* val;
842  NetAddr* encoded;
843  GULP;
844  // See if we can convert it to a NetAddr...
846  if ((encoded = netaddr_string_new(scan->value.v_string)) != NULL) {
848  val->u.addrvalue = encoded;
849  encoded = NULL;
850  }else{
852  val->u.strvalue = g_strdup(scan->value.v_string);
853  }
854  return val;
855  }
856 
857  case G_TOKEN_INT: { // Integer
859  GULP;
860  val->u.intvalue = scan->value.v_int64;
861  return val;
862  }
863  break;
864 
865  case G_TOKEN_FLOAT: { // Double value
867  GULP;
868  val->u.floatvalue = scan->value.v_float;
869  return val;
870  }
871  break;
872 
873  case G_TOKEN_SYMBOL: { // true, false, null
874  GULP;
875  if (strcmp(scan->value.v_string, "true") == 0 || strcmp(scan->value.v_string, "false") == 0) {
877  val->u.intvalue = (strcmp(scan->value.v_string, "true") == 0);
878  return val;
879  }else if (strcmp(scan->value.v_string, "null") == 0) {
881  }else{
882  SYNERROR(scan, G_TOKEN_NONE, NULL, "- expecting JSON value");
883  // Syntax error
884  return NULL;
885  }
886  }
887  break;
888 
889 
890  case G_TOKEN_LEFT_CURLY:{ // Object
891  ConfigValue* val;
892  ConfigContext* child;
893  child = _configcontext_JSON_parse_object(scan);
894  if (child == NULL) {
895  // Syntax error - detected by child object
896  return NULL;
897  }
899  val->u.cfgctxvalue = child;
900  return val;
901  }
902  break;
903 
904 
905  case G_TOKEN_LEFT_BRACE: { // Array
906  ConfigValue* val;
907  GSList* child = NULL;
908  if (!_configcontext_JSON_parse_array(scan, &child)) {
909  // Syntax error - detected by child object
910  return NULL;
911  }
913  val->u.arrayvalue = child;
914  return val;
915  }
916  break;
917 
918  // Things we don't support...
919  default:
920  // Syntax error
921  g_warning("Got token type %u", g_scanner_get_next_token(scan));
922  //GULP;
923  SYNERROR(scan, G_TOKEN_NONE, NULL, "Unexpected symbol.");
924  return NULL;
925  }
926  /*NOTREACHED*/
927  g_warning("Got token type %u", g_scanner_get_next_token(scan));
928  SYNERROR(scan, G_TOKEN_NONE, NULL, "Unexpected symbol.");
929  return NULL;
930 }
931 FSTATIC gboolean
932 _configcontext_JSON_parse_array(GScanner* scan, GSList** retval)
933 {
934  *retval = NULL;
935  if (g_scanner_peek_next_token(scan) != G_TOKEN_LEFT_BRACE) {
936  GULP;
937  SYNERROR(scan, G_TOKEN_LEFT_BRACE, NULL, NULL);
938  // Syntax error
939  return FALSE;
940  }
941  GULP; // Swallow left square bracket (G_TOKEN_LEFT_BRACE)
942  while (g_scanner_peek_next_token(scan) != G_TOKEN_RIGHT_BRACE
943  && !g_scanner_eof(scan)) {
944  ConfigValue * value;
945 
946  // Parse the value
947  value = _configcontext_JSON_parse_value(scan);
948  if (value == NULL) {
949  if (*retval != NULL) {
951  *retval = NULL;
952  return FALSE;
953  }
954  }else{
955  *retval = g_slist_append(*retval, value);
956  }
957  // Expect a comma
958  if (g_scanner_peek_next_token(scan) == G_TOKEN_COMMA) {
959  GULP;
960  }else if (g_scanner_peek_next_token(scan) != G_TOKEN_RIGHT_BRACE) {
961  SYNERROR(scan, G_TOKEN_RIGHT_BRACE, NULL, NULL);
962  GULP;
963  return FALSE;
964  }
965  }
966  if (g_scanner_peek_next_token(scan) == G_TOKEN_RIGHT_BRACE) {
967  GULP;
968  return TRUE;
969  }
970  SYNERROR(scan, G_TOKEN_RIGHT_BRACE, NULL, NULL);
971  return FALSE;
972 }