Add support for config file parsing and watching
[ric-plt/xapp-frame-cpp.git] / src / json / jwrapper.c
1 // vi: ts=4 sw=4 noet:
2 /*
3 ==================================================================================
4         Copyright (c) 2020 Nokia
5         Copyright (c) 2020 AT&T Intellectual Property.
6
7    Licensed under the Apache License, Version 2.0 (the "License");
8    you may not use this file except in compliance with the License.
9    You may obtain a copy of the License at
10
11        http://www.apache.org/licenses/LICENSE-2.0
12
13    Unless required by applicable law or agreed to in writing, software
14    distributed under the License is distributed on an "AS IS" BASIS,
15    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16    See the License for the specific language governing permissions and
17    limitations under the License.
18 ==================================================================================
19 */
20
21 /*
22         Mnemonic:       jwrapper.c
23         Abstract:       A wrapper interface to the jsmn library which makes it a bit easier
24                                 to use.  Parses a json string capturing the contents in a symtab.
25
26                                 This code is based on the AT&T VFd open source library available
27                                 on github.com/att/vfd.  The changes are mostly to port to the
28                                 RMR version of symtab from VFd's version.
29
30         Author:         E. Scott Daniels
31         Date:           26 June 2020
32
33 */
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <stdint.h>
39
40 #ifndef DEBUG
41         #define DEBUG 0
42 #endif
43
44 #define JSMN_STATIC 1           // jsmn no longer builds into a library; this pulls as static functions
45 #include <jsmn.h>
46 //#include <../../ext/jsmn/jsmn.h>
47
48 #include <rmr/rmr_symtab.h>
49
50 #define JSON_SYM_NAME   "_jw_json_string"
51 #define MAX_THINGS              1024 * 4        // max objects/elements
52
53 #define PT_UNKNOWN              0                       // primative types; unknown for non prim
54 #define PT_VALUE                1
55 #define PT_BOOL                 2
56 #define PT_NULL                 3
57 #define PT_STRING               4
58
59 #define OBJ_SPACE               1                       // space in the symbol table where json bits are stashed
60 #define MGT_SPACE               2                       // non-json objects in the hash (management things)
61
62 extern void jw_nuke( void* st );
63
64 // ---------------------------------------------------------------------------------------
65
66 /*
67         This is what we will manage in the symtab. Right now we store all values (primatives)
68         as double, but we could be smarter about it and look for a decimal. Unsigned and
69         differences between long, long long etc are tough.
70 */
71 typedef struct jthing {
72         int jsmn_type;                          // propigated type from jsmn (jsmn constants)
73         int prim_type;                          // finer grained primative type (bool, null, value)
74         int     nele;                                   // number of elements if applies
75         union {
76                 double fv;
77                 void *pv;
78         } v;
79 } jthing_t;
80
81
82 /*
83         Given the json token, 'extract' the element by marking the end with a
84         nil character, and returning a pointer to the start.  We do this so that
85         we don't create a bunch of small buffers that must be found and freed; we
86         can just release the json string and we'll be done (read won't leak).
87 */
88 static char* extract( char* buf, jsmntok_t *jtoken ) {
89         buf[jtoken->end] = 0;
90         return &buf[jtoken->start];
91 }
92
93 /*
94         create a new jthing and add a reference to it in the symbol table st.
95         sets the number of elements to 1 by default.
96 */
97 static jthing_t *mk_thing( void *st, char *name, int jsmn_type ) {
98         jthing_t        *jtp = NULL;
99
100         if( st != NULL &&
101                 name != NULL &&
102                 (jtp = (jthing_t *) malloc( sizeof( *jtp ) )) != NULL ) {
103
104                 if( DEBUG ) {
105                         fprintf( stderr, "<DBUG> jwrapper adding: %s type=%d\n", name, jsmn_type );
106                 }
107
108                 jtp->jsmn_type = jsmn_type;
109                 jtp->prim_type = PT_UNKNOWN;                    // caller must set this
110                 jtp->nele = 1;
111                 jtp->v.fv = 0;
112
113                 rmr_sym_put( st, name, OBJ_SPACE, jtp );
114         } else {
115                 fprintf( stderr, "[WARN] jwrapper: unable to create '%s' type=%d\n", name, jsmn_type );
116         }
117
118         return jtp;
119 }
120
121
122 /*
123         Find the named array. Returns a pointer to the jthing that represents
124         the array (type, size and pointer to actual array of jthings).
125         Returns nil pointer if the named thing isn't there or isn't an array.
126 */
127 static jthing_t* suss_array( void* st, const char* name ) {
128         jthing_t* jtp = NULL;                                                   // thing that is referenced by the symtab
129
130         if( st != NULL &&
131                 name != NULL &&
132                 (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
133
134                 jtp =  jtp->jsmn_type == JSMN_ARRAY  ? jtp : NULL;
135         }
136
137         return jtp;
138 }
139
140 /*
141         Suss an array from the hash and return the ith element.
142 */
143 static jthing_t* suss_element( void* st, const char* name, int idx ) {
144         jthing_t* jtp;                                                                  // thing that is referenced by the symtab
145         jthing_t* jarray;
146         jthing_t* rv = NULL;
147
148         if( (jtp = suss_array( st, name )) != NULL &&           // have pointer
149                 idx >= 0 &&                                                                             // and in range
150                 idx < jtp->nele ) {
151
152                 if( (jarray = jtp->v.pv)  != NULL ) {
153                         rv = &jarray[idx];
154                 }
155         }
156
157         return rv;
158 }
159
160
161 /*
162         Invoked for each thing in the symtab; we free the things that actually point to
163         allocated data (e.g. arrays) and recurse to handle objects.
164 */
165 static void nix_things( void* st, void* se, const char* name,  void* ele, void *data ) {
166         jthing_t*       j;
167         jthing_t*       jarray;
168         int i;
169
170         j = (jthing_t *) ele;
171         if( j ) {
172                 switch( j->jsmn_type ) {
173                         case JSMN_ARRAY:
174                                 if( (jarray = (jthing_t *) j->v.pv)  != NULL ) {
175                                         for( i = 0; i < j->nele; i++ ) {                                        // must look for embedded objects
176                                                 if( jarray[i].jsmn_type == JSMN_OBJECT ) {
177                                                         jw_nuke( jarray[i].v.pv );
178                                                         jarray[i].jsmn_type = JSMN_UNDEFINED;                   // prevent accidents
179                                                 }
180                                         }
181
182                                         free( j->v.pv );                        // must free the array (arrays aren't nested, so all things in the array don't reference allocated mem)
183                                         free( j );
184                                 }
185                                 break;
186
187                         case JSMN_OBJECT:                                                       // delete the sub symtab
188                                 jw_nuke( j->v.pv );
189                                 j->jsmn_type = JSMN_UNDEFINED;                  // prevent a double free
190                                 free( j );
191                                 break;
192
193                         case JSMN_STRING:
194                         case JSMN_PRIMITIVE:
195                                 free( j );
196                                 break;
197                 }
198         }
199 }
200
201 /*
202         Nix non-json things that are also in the hash.
203 */
204 static void nix_mgt( void* st, void* se, const char* name,  void* ele, void *data ) {
205         free( ele );
206 }
207
208 /*
209         Invoked for each thing and prints what we can to stderr.
210 */
211 static void dump_things( void* st, void* se, const char* name,  void* ele, void *data ) {
212         jthing_t*       j;
213         jthing_t*       jarray;
214         int i;
215
216         j = (jthing_t *) ele;
217         if( j ) {
218                 fprintf( stderr, "<DBUG> jwrapper: element '%s' has ptype %d, jsmn type %d\n", name, j->prim_type, j->jsmn_type );
219         } else {
220                 fprintf( stderr, "<DBUG> jwrapper: element has no data: '%s'\n", name );
221         }
222 }
223
224 /*
225         Real work for parsing an object ({...}) from the json.   Called by jw_new() and
226         recurses to deal with sub-objects.
227 */
228 void* parse_jobject( void* st, char *json, char* prefix ) {
229         jthing_t        *jtp;                   // json thing that we just created
230         int             i;
231         int             n;
232         char    *name;                          // name in the json
233         char    *data;                          // data string from the json
234         jthing_t*       jarray;                 // array of jthings we'll coonstruct
235         int             size;
236         int             osize;
237         int             njtokens;                       // tokens actually sussed out
238         jsmn_parser jp;                         // 'parser' object
239         jsmntok_t *jtokens;                     // pointer to tokens returned by the parser
240         char    pname[1024];            // name with prefix
241         char    wbuf[256];                      // temp buf to build a working name in
242         char*   dstr;                           // dup'd string
243
244         jsmn_init( &jp );                       // does this have a failure mode?
245
246         jtokens = (jsmntok_t *) malloc( sizeof( *jtokens ) * MAX_THINGS );
247         if( jtokens == NULL ) {
248                 fprintf( stderr, "[CRI] jwrapper: cannot allocate tokens array\n" );
249                 return NULL;
250         }
251
252         njtokens = jsmn_parse( &jp, json, strlen( json ), jtokens, MAX_THINGS );
253
254         if( jtokens[0].type != JSMN_OBJECT ) {                          // if it's not an object then we can't parse it.
255                 fprintf( stderr, "[WARN] jwrapper: badly formed json; initial opening bracket ({) not detected\n" );
256                 rmr_sym_free( st );
257                 free( jtokens );
258                 return NULL;
259         }
260
261         if( DEBUG ) {
262                 for( i = 1; i < njtokens-1; i++ ) {
263                         fprintf( stderr, "<DBUG> %4d: size=%d start=%d end=%d %s\n", i, jtokens[i].size, jtokens[i].start, jtokens[i].end, extract( json, &jtokens[i] ) );
264                 }
265         }
266
267         for( i = 1; i < njtokens-1; i++ ) {                                     // we'll silently skip the last token if it's "name" without a value
268                 if( jtokens[i].type != JSMN_STRING ) {
269                         fprintf( stderr, "[WARN] jwrapper: badly formed json [%d]; expected name (string) found type=%d %s\n", i, jtokens[i].type, extract( json, &jtokens[i] ) );
270                         rmr_sym_free( st );
271                         free( jtokens );
272                         return NULL;
273                 }
274                 name = extract( json, &jtokens[i] );
275                 if( *prefix != 0 ) {
276                         snprintf( pname, sizeof( pname ), "%s.%s", prefix, name );
277                         name = pname;
278                 }
279
280                 size = jtokens[i].size;
281
282                 i++;                                                                            // at the data token now
283                 switch( jtokens[i].type ) {
284                         case JSMN_OBJECT:                               // save object in two ways: as an object 'blob' and in the current symtab using name as a base (original)
285                                 dstr = strdup( extract( json, &jtokens[i] ) );
286                                 snprintf( wbuf, sizeof( wbuf ), "%s_json", name );      // must stash the json string in the symtab for clean up during nuke
287                                 rmr_sym_put( st, wbuf, MGT_SPACE, dstr );
288
289                                 parse_jobject( st, dstr, name );                                        // recurse to add the object as objectname.xxxx elements
290
291                                 if( (jtp = mk_thing( st, name, jtokens[i].type )) != NULL &&            // create thing and reference it in current symtab
292                                         (jtp->v.pv = (void *) rmr_sym_alloc( 255 ) ) != NULL ) {                // object is just a blob
293
294                                         dstr = strdup( extract( json, &jtokens[i] ) );
295                                         rmr_sym_put( jtp->v.pv, JSON_SYM_NAME, MGT_SPACE, dstr );               // must stash json so it is freed during nuke()
296                                         parse_jobject( jtp->v.pv,  dstr, "" );                                                  // recurse acorss the string and build a new symtab
297
298                                         size = jtokens[i].end;                                                                                  // done with them, we need to skip them
299                                         i++;
300                                         while( i < njtokens-1  &&  jtokens[i].end < size ) {
301                                                 if( DEBUG ){
302                                                         fprintf( stderr, "\tskip: [%d] object element start=%d end=%d (%s)\n",
303                                                                 i, jtokens[i].start, jtokens[i].end, extract( json, &jtokens[i])  );
304                                                 }
305                                                 i++;
306                                         }
307
308                                         i--;                                            // must allow loop to bump past the last
309                                 }
310                                 break;
311
312                         case JSMN_ARRAY:
313                                 size = jtokens[i].size;         // size is burried here, and not with the name
314                                 jtp = mk_thing( st, name, jtokens[i].type );
315
316                                 i++;                    // skip first ele; it is the whole array string which I don't grock the need for, but it's their code...
317                                 if( jtp == NULL ) {
318                                         fprintf( stderr, "[WARN] jwrapper: memory alloc error processing element [%d] in json\n", i );
319                                         rmr_sym_free( st );
320                                         free( jtokens );
321                                         return NULL;
322                                 }
323                                 jarray = jtp->v.pv = (jsmntok_t *) malloc( sizeof( *jarray ) * size );          // allocate the array
324                                 memset( jarray, 0, sizeof( *jarray ) * size );
325                                 jtp->nele = size;
326
327                                 for( n = 0; n < size; n++ ) {                                                           // for each array element
328                                         jarray[n].prim_type      = PT_UNKNOWN;                                          // assume not primative type
329                                         switch( jtokens[i+n].type ) {
330
331                                                 case JSMN_OBJECT:
332                                                         jarray[n].v.pv = (void *) rmr_sym_alloc( 255 );
333                                                         if( jarray[n].v.pv != NULL ) {
334                                                                 jarray[n].jsmn_type = JSMN_OBJECT;
335                                                                 parse_jobject( jarray[n].v.pv,  extract( json, &jtokens[i+n]  ), "" );          // recurse acorss the string and build a new symtab
336                                                                 osize = jtokens[i+n].end;                                                                       // done with them, we need to skip them
337                                                                 i++;
338                                                                 while( i+n < njtokens-1  &&  jtokens[n+i].end < osize ) {
339                                                                         i++;
340                                                                 }
341                                                                 i--;                                    // allow incr at loop end
342                                                         }
343                                                         break;
344
345                                                 case JSMN_ARRAY:
346                                                         fprintf( stderr, "[WARN] jwrapper: [%d] array element %d is not valid type (array) is not string or primative\n", i, n );
347                                                         n += jtokens[i+n].size;                 // this should skip the nested array
348                                                         free( jtp );
349                                                         free( jarray );
350                                                         jarray = NULL;
351                                                         break;
352
353                                                 case JSMN_STRING:
354                                                         data = extract( json, &jtokens[i+n] );
355                                                         jarray[n].v.pv = (void *) data;
356                                                         jarray[n].prim_type = PT_STRING;
357                                                         jarray[n].jsmn_type = JSMN_STRING;
358                                                         break;
359
360                                                 case JSMN_PRIMITIVE:
361                                                         data = extract( json, &jtokens[i+n] );
362                                                         switch( *data ) {
363                                                                 case 'T':
364                                                                 case 't':
365                                                                         jarray[n].v.fv = 1;
366                                                                         jarray[n].prim_type      = PT_BOOL;
367                                                                         break;
368
369                                                                 case 'F':
370                                                                 case 'f':
371                                                                         jarray[n].prim_type      = PT_BOOL;
372                                                                         jarray[n].v.fv = 0;
373                                                                         break;
374
375                                                                 case 'N':                                                                               // assume null, nil, or some variant
376                                                                 case 'n':
377                                                                         jarray[n].prim_type      = PT_NULL;
378                                                                         jarray[n].v.fv = 0;
379                                                                         break;
380
381                                                                 default:
382                                                                         jarray[n].prim_type      = PT_VALUE;
383                                                                         jarray[n].v.fv = strtod( data, NULL );          // store all numerics as double
384                                                                         break;
385                                                         }
386
387                                                         jarray[n].jsmn_type = JSMN_PRIMITIVE;
388                                                         break;
389
390                                                 case JSMN_UNDEFINED:
391                                                         // fallthrough
392                                                 default:
393                                                         fprintf( stderr, "[WARN] jwrapper: [%d] array element %d is not valid type (unknown=%d) is not string or primative\n", i, n, jtokens[i].type  );
394                                                         rmr_sym_free( st );
395                                                         free( jtokens );
396                                                         return NULL;
397                                                         break;
398                                         }
399                                 }
400
401                                 i += size - 1;          // must allow loop to push to next
402                                 break;
403
404                         case JSMN_STRING:
405                                 data = extract( json, &jtokens[i] );
406                                 if( (jtp = mk_thing( st, name, jtokens[i].type )) != NULL ) {
407                                         jtp->prim_type = PT_STRING;
408                                         jtp->v.pv =  (void *) data;                                             // just point into the large json string
409                                 }
410                                 break;
411
412                         case JSMN_PRIMITIVE:
413                                 data = extract( json, &jtokens[i] );
414                                 if( (jtp = mk_thing( st, name, jtokens[i].type )) != NULL ) {
415                                         switch( *data ) {                                                               // assume T|t is true and F|f is false
416                                                 case 'T':
417                                                 case 't':
418                                                         jtp->prim_type = PT_BOOL;
419                                                         jtp->v.fv = 1;
420                                                         break;
421
422                                                 case 'F':
423                                                 case 'f':
424                                                         jtp->prim_type = PT_BOOL;
425                                                         jtp->v.fv = 0;
426                                                         break;
427
428                                                 case 'N':                                                                       // Null or some form of that
429                                                 case 'n':
430                                                         jtp->prim_type = PT_NULL;
431                                                         jtp->v.fv = 0;
432                                                         break;
433
434                                                 default:
435                                                         jtp->prim_type = PT_VALUE;
436                                                         jtp->v.fv = strtod( data, NULL );               // store all numerics as double
437                                                         break;
438                                         }
439                                 }
440                                 break;
441
442                         default:
443                                 fprintf( stderr, "[WARN] jwrapper: element [%d] is undefined or of unknown type\n", i );
444                                 break;
445                 }
446         }
447
448         free( jtokens );
449         return st;
450 }
451
452 // --------------- public functions -----------------------------------------------------------------
453
454 /*
455         Destroy all operating structures assocaited with the symtab pointer passed in.
456 */
457 extern void jw_nuke( void* st ) {
458         char*   buf;                                    // pointer to the original json to free
459
460         if( st != NULL ) {
461                 rmr_sym_foreach_class( st, OBJ_SPACE, nix_things, NULL );       // free any json thing that the symtab references
462                 rmr_sym_foreach_class( st, MGT_SPACE, nix_mgt, NULL );          // free management things
463                 rmr_sym_free( st );                                                                                     // free the symtab itself
464         }
465 }
466
467 /*
468         Scan the given st and write some useful (?) info to stderr.
469 */
470 extern void jw_dump( void* st ) {
471         if( st != NULL ) {
472                 rmr_sym_foreach_class( st, OBJ_SPACE, dump_things, NULL );
473         } else {
474                 fprintf( stderr, "<DBUG> jwrapper: dump: no table\n" );
475         }
476 }
477
478 /*
479         Given a json string, parse it, and put the things into a symtab.
480         return the symtab pointer to the caller. They pass the symtab
481         pointer back to the various get functions.
482
483         This is the entry point. It sets up the symbol table and invokes the parse object
484         funtion to start at the first level. Parse object will recurse for nested objects
485         if present.
486 */
487 extern void* jw_new( const char* json ) {
488         void    *st = NULL;                     // symbol table
489         char*   djson;                          // dup so we can save it
490         void*   rp = NULL;                      // return value
491
492         if( json != NULL && (st = rmr_sym_alloc( MAX_THINGS/4 )) != NULL ) {
493                 djson = strdup( json );                                                                                                 // allows user to free/overlay their buffer as needed
494                 rp =  parse_jobject( st,  djson, "" );                  // empty prefix for the root object; parse_jo will clean up and free st
495                 if( rp == NULL ) {
496                         free( djson );
497                 } else {
498                         rmr_sym_put( st, (unsigned char *) JSON_SYM_NAME, MGT_SPACE, djson );   // must have a reference to the string until symtab is trashed
499                 }
500         }
501
502         return rp;
503 }
504
505 /*
506         Returns true (1) if the named field is missing.
507 */
508 extern int jw_missing( void* st, const char* name ) {
509         int rv = 0;
510
511         if( st != NULL && name != NULL ) {
512                 rv = rmr_sym_get( st, name, OBJ_SPACE ) == NULL;
513         }
514
515         return rv;
516 }
517
518 /*
519         Returns true (1) if the named field is in the blob;
520 */
521 extern int jw_exists( void* st, const char* name ) {
522         int rv = 0;
523
524         if( st != NULL && name != NULL ) {
525                 rv =  rmr_sym_get( st, name, OBJ_SPACE ) != NULL;
526         }
527
528         return rv;
529 }
530
531 /*
532         Returns true (1) if the primative type is value (double).
533 */
534 extern int jw_is_value( void* st, const char* name ) {
535         jthing_t* jtp;                                                                  // thing that is referenced by the symtab
536         int rv = 0;
537
538         if( st != NULL &&
539                 name != NULL &&
540                 (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
541
542                 rv = jtp->prim_type == PT_VALUE;
543         }
544
545         return rv;
546 }
547 /*
548         Returns true (1) if the primative type is string.
549 */
550 extern int jw_is_string( void* st, const char* name ) {
551         jthing_t* jtp;                                                                  // thing that is referenced by the symtab
552         int rv = 0;
553
554         if( st != NULL &&
555                 name != NULL &&
556                 (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
557
558                 rv = jtp->prim_type == PT_STRING;
559         }
560
561         return rv;
562 }
563
564 /*
565         Returns true (1) if the primative type is boolean.
566 */
567 extern int jw_is_bool( void* st, const char* name ) {
568         jthing_t* jtp;                                                                  // thing that is referenced by the symtab
569         int             rv = 0;
570
571         if( st != NULL &&
572                 name != NULL &&
573                 (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
574
575                 rv =  jtp->prim_type == PT_BOOL;
576         }
577
578         return rv;
579 }
580
581 /*
582         Returns true (1) if the primative type was a 'null' type.
583 */
584 extern int jw_is_null( void* st, const char* name ) {
585         jthing_t* jtp;                                                                  // thing that is referenced by the symtab
586         int             rv = 0;
587
588         if( st != NULL &&
589                 name != NULL &&
590                 (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
591
592                 rv = jtp->prim_type == PT_NULL;
593         }
594
595         return rv;
596 }
597
598 /*
599         Look up the name in the symtab and return the string (data).
600 */
601 extern char* jw_string( void* st, const char* name ) {
602         jthing_t* jtp;                                                                  // thing that is referenced by the symtab
603         char*           rv = NULL;
604
605         if( st != NULL &&
606                 name != NULL &&
607                 (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
608
609                 if( jtp->jsmn_type == JSMN_STRING ) {
610                         rv = (char *) jtp->v.pv;
611                 }
612         }
613
614         return rv;
615 }
616
617 /*
618         Look up name and return the value.
619 */
620 extern double jw_value( void* st, const char* name ) {
621         jthing_t* jtp;                                                                  // thing that is referenced by the symtab
622         double  rv = 0.0;
623
624         if( st != NULL &&
625                 name != NULL &&
626                 (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
627
628                 if( jtp->jsmn_type == JSMN_PRIMITIVE ) {
629                         rv = jtp->v.fv;
630                 }
631         }
632
633         return rv;
634 }
635
636 /*
637         Look up name and return the blob (symtab).
638 */
639 extern void* jw_blob( void* st, const char* name ) {
640         jthing_t* jtp;                                                                  // thing that is referenced by the symtab
641         void*   rv = NULL;
642
643         if( st != NULL &&
644                 name != NULL &&
645                 (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
646
647                 if( jtp->jsmn_type == JSMN_OBJECT ) {
648                         rv = (void *) jtp->v.pv;
649                 }
650         }
651
652         return rv;
653 }
654
655 /*
656         Look up the element and return boolean state; This takes the C approach and
657         returns true/false based on the value.
658 */
659 extern int jw_bool_ele( void* st, const char* name, int idx ) {
660         jthing_t* jtp;                                                                  // thing that is referenced by the symtab entry
661         int             rv = 0;
662
663         if( st != NULL &&
664                 name != NULL &&
665                 (jtp = suss_element( st, name, idx )) != NULL ) {
666
667                         rv = !! ((int) jtp->v.fv);
668         }
669
670         return rv;
671 }
672 /*
673         Look up array element as a string. Returns NULL if:
674                 name is not an array
675                 name is not in the hash
676                 index is out of range
677                 element is not a string
678 */
679 extern char* jw_string_ele( void* st, const char* name, int idx ) {
680         jthing_t* jtp;                                                                  // thing that is referenced by the symtab entry
681         char*           rv = NULL;
682
683         if( st != NULL &&
684                 name != NULL &&
685                 (jtp = suss_element( st, name, idx )) != NULL ) {
686
687                 if( jtp->jsmn_type == JSMN_STRING ) {
688                         rv = (char *) jtp->v.pv;
689                 }
690         }
691
692         return rv;
693 }
694
695 /*
696         Look up array element as a value. Returns 0 if:
697                 name is not an array
698                 name is not in the hash
699                 index is out of range
700                 element is not a value
701 */
702 extern double jw_value_ele( void* st, const char* name, int idx ) {
703         jthing_t*       jtp;                                                    // thing that is referenced by the symtab entry
704         double          rv = 0.0;
705
706         if( st != NULL &&
707                 name != NULL &&
708                 (jtp = suss_element( st, name, idx )) != NULL ) {
709
710                 if( jtp->prim_type == PT_VALUE ) {
711                         rv = jtp->v.fv;
712                 }
713         }
714
715         return rv;
716 }
717 /*
718         Look up the element and check to see if it is a string.
719         Return true (1) if it is.
720 */
721 extern int jw_is_string_ele( void* st, const char* name, int idx ) {
722         jthing_t* jtp;                                                                  // thing that is referenced by the symtab entry
723         int             rv = 0;
724
725         if( st != NULL &&
726                 name != NULL &&
727                 (jtp = suss_element( st, name, idx )) != NULL ) {
728
729                         rv = jtp->prim_type == PT_STRING;
730         }
731
732         return rv;
733 }
734
735 /*
736         Look up the element and check to see if it is a value primative.
737         Return true (1) if it is.
738 */
739 extern int jw_is_value_ele( void* st, const char* name, int idx ) {
740         jthing_t* jtp;                                                                  // thing that is referenced by the symtab entry
741         int             rv = 0;
742
743         if( st != NULL &&
744                 name != NULL &&
745                 (jtp = suss_element( st, name, idx )) != NULL ) {
746
747                         rv = jtp->prim_type == PT_VALUE;
748         }
749
750         return rv;
751 }
752
753 /*
754         Look up the element and check to see if it is a boolean primative.
755         Return true (1) if it is.
756 */
757 extern int jw_is_bool_ele( void* st, const char* name, int idx ) {
758         jthing_t* jtp;                                                                  // thing that is referenced by the symtab entry
759         int             rv = 0;
760
761         if( st != NULL &&
762                 name != NULL &&
763                 (jtp = suss_element( st, name, idx )) != NULL ) {
764
765                         rv = jtp->prim_type == PT_BOOL;
766         }
767
768         return rv;
769 }
770
771 /*
772         Look up the element and check to see if it is a null primative.
773         Return true (1) if it is.
774 */
775 extern int jw_is_null_ele( void* st, const char* name, int idx ) {
776         jthing_t* jtp;                                                                  // thing that is referenced by the symtab entry
777         int             rv = 0;
778
779         if( st != NULL &&
780                 name != NULL &&
781                 (jtp = suss_element( st, name, idx )) != NULL ) {
782
783                         rv =  jtp->prim_type == PT_NULL;
784         }
785
786         return rv;
787 }
788
789 /*
790         Look up array element as an object. Returns NULL if:
791                 name is not an array
792                 name is not in the hash
793                 index is out of range
794                 element is not an object
795
796         An object in an array is a standalone symbol table. Thus the object
797         is treated differently than a nested object whose members are a
798         part of the parent namespace.  An object in an array has its own
799         namespace.
800 */
801 extern void* jw_obj_ele( void* st, const char* name, int idx ) {
802         jthing_t* jtp;                                                                  // thing that is referenced by the symtab entry
803         void*           rv = NULL;
804
805         if( st != NULL &&
806                 name != NULL &&
807                 (jtp = suss_element( st, name, idx )) != NULL ) {
808
809                 if( jtp->jsmn_type == JSMN_OBJECT ) {
810                         rv = (void *) jtp->v.pv;
811                 }
812         }
813
814         return rv;
815 }
816
817 /*
818         Return the size of the array named. Returns -1 if the thing isn't an array,
819         and returns the number of elements otherwise.
820 */
821 extern int jw_array_len( void* st, const char* name ) {
822         jthing_t* jtp;                                                                  // thing that is referenced by the symtab entry
823         int             rv = -1;
824
825         if( st != NULL &&
826                 name != NULL &&
827                 (jtp = suss_array( st, name )) != NULL ) {
828
829                 rv = jtp->nele;
830         }
831
832         return rv;
833 }