LCOV - code coverage report
Current view: top level - src/plugins/yajl - yajl_parse.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 160 175 91.4 %
Date: 2019-09-12 12:28:41 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  */
       8             : 
       9             : #include "yajl.h"
      10             : 
      11             : #include <errno.h>
      12             : #include <stdio.h>
      13             : #include <string.h>
      14             : 
      15             : #include <kdbease.h>
      16             : #include <kdberrors.h>
      17             : #include <kdbmacros.h>
      18             : #include <yajl/yajl_parse.h>
      19             : 
      20             : 
      21         471 : static void elektraYajlSetArrayLength (KeySet * ks, Key * current)
      22             : {
      23             :         // Update array length in array key
      24         471 :         cursor_t cursor = ksGetCursor (ks);
      25         471 :         Key * arrayKey = keyNew (keyName (current), KEY_END);
      26         471 :         keySetBaseName (arrayKey, 0);
      27         471 :         Key * foundKey = ksLookup (ks, arrayKey, 0);
      28         471 :         keySetMeta (foundKey, "array", keyBaseName (current));
      29         471 :         keyDel (arrayKey);
      30         471 :         ksSetCursor (ks, cursor);
      31         471 : }
      32             : 
      33             : /**
      34             :  @retval 0 if ksCurrent does not hold an array entry
      35             :  @retval 1 if the array entry will be used because its the first
      36             :  @retval 2 if a new array entry was created
      37             :  @retval -1 error in snprintf
      38             :  */
      39        2086 : static int elektraYajlIncrementArrayEntry (KeySet * ks)
      40             : {
      41        2086 :         Key * current = ksCurrent (ks);
      42        2086 :         const char * baseName = keyBaseName (current);
      43        2086 :         const char * meta = keyString (keyGetMeta (current, "array"));
      44        2086 :         if (!strcmp (meta, "empty"))
      45             :         {
      46         121 :                 current = keyNew (keyName (current), KEY_END);
      47         121 :                 keyAddName (current, "#0");
      48         121 :                 ksAppendKey (ks, current);
      49             : 
      50         121 :                 elektraYajlSetArrayLength (ks, current);
      51             : 
      52         121 :                 return 1;
      53             :         }
      54        1965 :         else if (baseName && *baseName == '#')
      55             :         {
      56             :                 // we are in an array
      57         350 :                 current = keyNew (keyName (current), KEY_END);
      58         350 :                 elektraArrayIncName (current);
      59         350 :                 ksAppendKey (ks, current);
      60             : 
      61         350 :                 elektraYajlSetArrayLength (ks, current);
      62             : 
      63         350 :                 return 2;
      64             :         }
      65             :         else
      66             :         {
      67             :                 // previous entry indicates this is not an array
      68             :                 return 0;
      69             :         }
      70             : }
      71             : 
      72          14 : static int elektraYajlParseNull (void * ctx)
      73             : {
      74          14 :         KeySet * ks = (KeySet *) ctx;
      75          14 :         elektraYajlIncrementArrayEntry (ks);
      76             : 
      77          14 :         Key * current = ksCurrent (ks);
      78             : 
      79          14 :         keySetBinary (current, NULL, 0);
      80             : 
      81             :         ELEKTRA_LOG_DEBUG ("parse null");
      82             : 
      83          14 :         return 1;
      84             : }
      85             : 
      86          50 : static int elektraYajlParseBoolean (void * ctx, int boolean)
      87             : {
      88          50 :         KeySet * ks = (KeySet *) ctx;
      89          50 :         elektraYajlIncrementArrayEntry (ks);
      90             : 
      91          50 :         Key * current = ksCurrent (ks);
      92             : 
      93          50 :         if (boolean == 1)
      94             :         {
      95          32 :                 keySetString (current, "true");
      96             :         }
      97             :         else
      98             :         {
      99          18 :                 keySetString (current, "false");
     100             :         }
     101          50 :         keySetMeta (current, "type", "boolean");
     102             : 
     103             :         ELEKTRA_LOG_DEBUG ("%d", boolean);
     104             : 
     105          50 :         return 1;
     106             : }
     107             : 
     108         303 : static int elektraYajlParseNumber (void * ctx, const char * stringVal, yajl_size_type stringLen)
     109             : {
     110         303 :         KeySet * ks = (KeySet *) ctx;
     111         303 :         elektraYajlIncrementArrayEntry (ks);
     112             : 
     113         303 :         Key * current = ksCurrent (ks);
     114             : 
     115         303 :         unsigned char delim = stringVal[stringLen];
     116         303 :         char * stringValue = (char *) stringVal;
     117         303 :         stringValue[stringLen] = '\0';
     118             : 
     119             :         ELEKTRA_LOG_DEBUG ("%s %zu", stringVal, stringLen);
     120             : 
     121         303 :         keySetString (current, stringVal);
     122         303 :         keySetMeta (current, "type", "double");
     123             : 
     124             :         // restore old character in buffer
     125         303 :         stringValue[stringLen] = delim;
     126             : 
     127         303 :         return 1;
     128             : }
     129             : 
     130         491 : static int elektraYajlParseString (void * ctx, const unsigned char * stringVal, yajl_size_type stringLen)
     131             : {
     132         491 :         KeySet * ks = (KeySet *) ctx;
     133         491 :         elektraYajlIncrementArrayEntry (ks);
     134             : 
     135         491 :         Key * current = ksCurrent (ks);
     136             : 
     137         491 :         unsigned char delim = stringVal[stringLen];
     138         491 :         char * stringValue = (char *) stringVal;
     139         491 :         stringValue[stringLen] = '\0';
     140             : 
     141             :         ELEKTRA_LOG_DEBUG ("%s %zu", stringVal, stringLen);
     142             : 
     143         491 :         keySetString (current, stringValue);
     144             : 
     145             :         // restore old character in buffer
     146         491 :         stringValue[stringLen] = delim;
     147         491 :         return 1;
     148             : }
     149             : 
     150         763 : static int elektraYajlParseMapKey (void * ctx, const unsigned char * stringVal, yajl_size_type stringLen)
     151             : {
     152         763 :         KeySet * ks = (KeySet *) ctx;
     153         763 :         elektraYajlIncrementArrayEntry (ks);
     154             : 
     155         763 :         Key * currentKey = keyNew (keyName (ksCurrent (ks)), KEY_END);
     156         763 :         keySetString (currentKey, 0);
     157             : 
     158         763 :         unsigned char delim = stringVal[stringLen];
     159         763 :         char * stringValue = (char *) stringVal;
     160         763 :         stringValue[stringLen] = '\0';
     161             : 
     162             :         ELEKTRA_LOG_DEBUG ("stringValue: %s currentKey: %s", stringValue, keyName (currentKey));
     163         763 :         if (currentKey && !strcmp (keyBaseName (currentKey), "___empty_map"))
     164             :         {
     165             :                 // remove old key
     166         286 :                 keyDel (ksLookup (ks, currentKey, KDB_O_POP));
     167             :                 // now we know the name of the object
     168         286 :                 keySetBaseName (currentKey, stringValue);
     169             :         }
     170             :         else
     171             :         {
     172             :                 // we entered a new pair (inside the previous object)
     173         477 :                 keySetBaseName (currentKey, stringValue);
     174             :         }
     175         763 :         ksAppendKey (ks, currentKey);
     176             : 
     177             :         // restore old character in buffer
     178         763 :         stringValue[stringLen] = delim;
     179             : 
     180         763 :         return 1;
     181             : }
     182             : 
     183         312 : static int elektraYajlParseStartMap (void * ctx)
     184             : {
     185         312 :         KeySet * ks = (KeySet *) ctx;
     186         312 :         elektraYajlIncrementArrayEntry (ks);
     187             : 
     188         312 :         Key * currentKey = ksCurrent (ks);
     189             : 
     190         312 :         Key * newKey = keyNew (keyName (currentKey), KEY_END);
     191             :         // add a pseudo element for empty map
     192         312 :         keyAddBaseName (newKey, "___empty_map");
     193         312 :         ksAppendKey (ks, newKey);
     194             : 
     195             :         ELEKTRA_LOG_DEBUG ("with new key %s", keyName (newKey));
     196             : 
     197         312 :         return 1;
     198             : }
     199             : 
     200         465 : static int elektraYajlParseEnd (void * ctx)
     201             : {
     202         465 :         KeySet * ks = (KeySet *) ctx;
     203         465 :         Key * currentKey = ksCurrent (ks);
     204             : 
     205         465 :         const char * meta = keyString (keyGetMeta (currentKey, "array"));
     206             :         // If array is still empty by the time we reach the end, replace with ""
     207         465 :         if (!strcmp (meta, "empty"))
     208             :         {
     209          32 :                 keySetMeta (currentKey, "array", "");
     210          32 :                 return 1;
     211             :         }
     212             : 
     213         433 :         Key * lookupKey = keyNew (keyName (currentKey), KEY_END);
     214         433 :         keySetBaseName (lookupKey, 0); // remove current baseName
     215             : 
     216             :         // lets point current to the correct place
     217         433 :         Key * foundKey = ksLookup (ks, lookupKey, 0);
     218             : 
     219             : #ifdef HAVE_LOGGER
     220             :         if (foundKey)
     221             :         {
     222             :                 ELEKTRA_LOG_DEBUG ("%s", keyName (foundKey));
     223             :         }
     224             :         else
     225             :         {
     226             :                 ELEKTRA_LOG_DEBUG ("did not find key %s", keyName (lookupKey));
     227             :         }
     228             : #else
     229             :         (void) foundKey; // foundKey is not used, but lookup is needed
     230             : #endif
     231             : 
     232         433 :         keyDel (lookupKey);
     233             : 
     234         433 :         return 1;
     235             : }
     236             : 
     237         153 : static int elektraYajlParseStartArray (void * ctx)
     238             : {
     239         153 :         KeySet * ks = (KeySet *) ctx;
     240         153 :         elektraYajlIncrementArrayEntry (ks);
     241             : 
     242         153 :         Key * currentKey = ksCurrent (ks);
     243             : 
     244         153 :         Key * newKey = keyNew (keyName (currentKey), KEY_END);
     245         153 :         keySetMeta (newKey, "array", "empty");
     246         153 :         ksAppendKey (ks, newKey);
     247             : 
     248             :         ELEKTRA_LOG_DEBUG ("with new key %s", keyName (newKey));
     249             : 
     250         153 :         return 1;
     251             : }
     252             : 
     253             : /**
     254             :  * @brief Remove all non-leaf keys except for arrays
     255             :  *
     256             :  * @param returned to remove the keys from
     257             :  */
     258          89 : static void elektraYajlParseSuppressNonLeafKeys (KeySet * returned)
     259             : {
     260          89 :         ksRewind (returned);
     261          89 :         Key * cur = ksNext (returned);
     262        1438 :         while (cur != NULL)
     263             :         {
     264        1349 :                 cursor_t cursor = ksGetCursor (returned);
     265             : 
     266        1349 :                 if (ksNext (returned) == NULL) break;
     267             : 
     268        1260 :                 Key * peekDup = keyDup (ksCurrent (returned));
     269        1260 :                 keySetBaseName (peekDup, 0);
     270             : 
     271        1260 :                 if (!strcmp (keyName (peekDup), keyName (cur)))
     272             :                 {
     273         433 :                         const char * baseName = keyBaseName (ksCurrent (returned));
     274             :                         // TODO: Add test for empty array check
     275         433 :                         if (strcmp (baseName, "#0"))
     276             :                         {
     277             :                                 ELEKTRA_LOG_DEBUG ("Removing non-leaf key %s", keyName (cur));
     278         312 :                                 keyDel (ksLookup (returned, cur, KDB_O_POP));
     279         312 :                                 ksSetCursor (returned, cursor);
     280             :                         }
     281             :                         else
     282             :                         {
     283             :                                 // Set array key to NULL to avoid empty ___dirdata entries
     284         121 :                                 keySetBinary (cur, NULL, 0);
     285             :                         }
     286             :                 }
     287             : 
     288        1260 :                 keyDel (peekDup);
     289        1260 :                 cur = ksCurrent (returned);
     290             :         }
     291          89 : }
     292             : 
     293             : /**
     294             :  * @brief Remove ___empty_map if thats the only thing which would be
     295             :  *        returned.
     296             :  *
     297             :  * @param returned to remove the key from
     298             :  */
     299          89 : static void elektraYajlParseSuppressEmptyMap (KeySet * returned, Key * parentKey)
     300             : {
     301             : 
     302          89 :         if (ksGetSize (returned) == 2)
     303             :         {
     304          10 :                 Key * lookupKey = keyDup (parentKey);
     305          10 :                 keyAddBaseName (lookupKey, "___empty_map");
     306          10 :                 Key * toRemove = ksLookup (returned, lookupKey, KDB_O_POP);
     307             : 
     308             : #ifdef HAVE_LOGGER
     309             :                 if (toRemove)
     310             :                 {
     311             :                         ELEKTRA_LOG_DEBUG ("remove %s", keyName (toRemove));
     312             :                 }
     313             :                 else
     314             :                 {
     315             :                         ksRewind (returned);
     316             :                         Key * cur;
     317             :                         while ((cur = ksNext (returned)) != 0)
     318             :                         {
     319             :                                 ELEKTRA_LOG_DEBUG ("key %s has value %s", keyName (cur), keyString (cur));
     320             :                         }
     321             : 
     322             :                         ELEKTRA_LOG_DEBUG ("did not find %s", keyName (lookupKey));
     323             :                         ksRewind (returned);
     324             :                 }
     325             : #endif
     326          10 :                 if (toRemove)
     327             :                 {
     328           0 :                         keyDel (toRemove);
     329             :                 }
     330          10 :                 keyDel (lookupKey);
     331             :         }
     332          89 : }
     333             : 
     334          86 : static inline KeySet * elektraGetModuleConfig (void)
     335             : {
     336          86 :         return ksNew (30, keyNew ("system/elektra/modules/yajl", KEY_VALUE, "yajl plugin waits for your orders", KEY_END),
     337             :                       keyNew ("system/elektra/modules/yajl/exports", KEY_END),
     338             :                       keyNew ("system/elektra/modules/yajl/exports/get", KEY_FUNC, elektraYajlGet, KEY_END),
     339             :                       keyNew ("system/elektra/modules/yajl/exports/set", KEY_FUNC, elektraYajlSet, KEY_END),
     340             : #include "readme_yajl.c"
     341             :                       keyNew ("system/elektra/modules/yajl/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END),
     342             :                       keyNew ("system/elektra/modules/yajl/config", KEY_END),
     343             :                       keyNew ("system/elektra/modules/yajl/config/", KEY_VALUE, "system", KEY_END),
     344             :                       keyNew ("system/elektra/modules/yajl/config/below", KEY_VALUE, "user", KEY_END), KS_END);
     345             : }
     346             : 
     347         175 : int elektraYajlGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned, Key * parentKey)
     348             : {
     349         175 :         if (!strcmp (keyName (parentKey), "system/elektra/modules/yajl"))
     350             :         {
     351          86 :                 KeySet * moduleConfig = elektraGetModuleConfig ();
     352          86 :                 ksAppend (returned, moduleConfig);
     353          86 :                 ksDel (moduleConfig);
     354          86 :                 return 1;
     355             :         }
     356             : 
     357          89 :         yajl_callbacks callbacks = { elektraYajlParseNull,
     358             :                                      elektraYajlParseBoolean,
     359             :                                      NULL,
     360             :                                      NULL,
     361             :                                      elektraYajlParseNumber,
     362             :                                      elektraYajlParseString,
     363             :                                      elektraYajlParseStartMap,
     364             :                                      elektraYajlParseMapKey,
     365             :                                      elektraYajlParseEnd,
     366             :                                      elektraYajlParseStartArray,
     367             :                                      elektraYajlParseEnd };
     368             : 
     369          89 :         ksAppendKey (returned, keyNew (keyName ((parentKey)), KEY_END));
     370             : 
     371             : #if YAJL_MAJOR == 1
     372             :         yajl_parser_config cfg = { 1, 1 };
     373             :         yajl_handle hand = yajl_alloc (&callbacks, &cfg, NULL, returned);
     374             : #else
     375          89 :         yajl_handle hand = yajl_alloc (&callbacks, NULL, returned);
     376          89 :         yajl_config (hand, yajl_allow_comments, 1);
     377             : #endif
     378             : 
     379          89 :         int errnosave = errno;
     380             :         unsigned char fileData[65536];
     381          89 :         int done = 0;
     382          89 :         FILE * fileHandle = fopen (keyString (parentKey), "r");
     383          89 :         if (!fileHandle)
     384             :         {
     385           0 :                 yajl_free (hand);
     386           0 :                 ELEKTRA_SET_ERROR_GET (parentKey);
     387           0 :                 errno = errnosave;
     388           0 :                 return -1;
     389             :         }
     390             : 
     391         267 :         while (!done)
     392             :         {
     393         178 :                 yajl_size_type rd = fread ((void *) fileData, 1, sizeof (fileData) - 1, fileHandle);
     394         178 :                 if (rd == 0)
     395             :                 {
     396          89 :                         if (!feof (fileHandle))
     397             :                         {
     398           0 :                                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Error while reading file: %s", keyString (parentKey));
     399           0 :                                 fclose (fileHandle);
     400           0 :                                 yajl_free (hand);
     401           0 :                                 return -1;
     402             :                         }
     403             :                         done = 1;
     404             :                 }
     405         178 :                 fileData[rd] = 0;
     406             : 
     407             :                 yajl_status stat;
     408         178 :                 if (done)
     409             :                 {
     410             : #if YAJL_MAJOR == 1
     411             :                         stat = yajl_parse_complete (hand);
     412             : #else
     413          89 :                         stat = yajl_complete_parse (hand);
     414             : #endif
     415             :                 }
     416             :                 else
     417             :                 {
     418          89 :                         stat = yajl_parse (hand, fileData, rd);
     419             :                 }
     420         178 :                 int test_status = (stat != yajl_status_ok);
     421             : #if YAJL_MAJOR == 1
     422             :                 test_status = test_status && (stat != yajl_status_insufficient_data);
     423             : #endif
     424         178 :                 if (test_status)
     425             :                 {
     426           0 :                         unsigned char * str = yajl_get_error (hand, 1, fileData, rd);
     427           0 :                         ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Yajl parse error happened. Reason: %s", (char *) str);
     428           0 :                         yajl_free_error (hand, str);
     429           0 :                         yajl_free (hand);
     430           0 :                         fclose (fileHandle);
     431             : 
     432           0 :                         return -1;
     433             :                 }
     434             :         }
     435             : 
     436          89 :         yajl_free (hand);
     437          89 :         fclose (fileHandle);
     438          89 :         elektraYajlParseSuppressNonLeafKeys (returned);
     439          89 :         elektraYajlParseSuppressEmptyMap (returned, parentKey);
     440             : 
     441          89 :         return 1; /* success */
     442             : }

Generated by: LCOV version 1.13