LCOV - code coverage report
Current view: top level - src/plugins/yajl - yajl_gen.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 107 125 85.6 %
Date: 2019-09-12 12:28:41 Functions: 7 7 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_gen.h"
      10             : 
      11             : #include <errno.h>
      12             : 
      13             : 
      14             : /**
      15             :  * @brief Return the first character of next name level
      16             :  *
      17             :  * @pre it must be safe to look at pnext+size
      18             :  * @pre string must be null terminated
      19             :  *
      20             :  * @param pnext pointer to current level
      21             :  * @param size size of name in that level
      22             :  *
      23             :  * @return #lookahead
      24             :  */
      25        1305 : lookahead_t elektraLookahead (const char * pnext, size_t size)
      26             : {
      27        1305 :         lookahead_t lookahead = LOOKAHEAD_END; // found end
      28        1305 :         if (*(pnext + size) == '/')
      29             :         {
      30             :                 // we are not at end, so we can look one further
      31         708 :                 if (*(pnext + size + 1) == '#')
      32             :                 {
      33         270 :                         if (!strcmp (pnext + size + 1, "###empty_array"))
      34             :                         {
      35             :                                 lookahead = LOOKAHEAD_EMPTY_ARRAY;
      36             :                         }
      37             :                         else
      38             :                         {
      39         210 :                                 lookahead = LOOKAHEAD_ARRAY;
      40             :                         }
      41             :                 }
      42             :                 else
      43             :                 {
      44         438 :                         if (!strcmp (pnext + size + 1, "___empty_map"))
      45             :                         {
      46             :                                 lookahead = LOOKAHEAD_EMPTY_MAP;
      47             :                         }
      48             :                         else
      49             :                         {
      50         390 :                                 lookahead = LOOKAHEAD_MAP;
      51             :                         }
      52             :                 }
      53             :         }
      54             : 
      55             :         // / and not NULL for nice printing
      56        1305 :         return lookahead; // found End
      57             : }
      58             : 
      59             : /**
      60             :  * @brief Implements special handling for last element of key name
      61             :  *
      62             :  * @pre g is in a map or array
      63             :  *
      64             :  * @post g need value as next step
      65             :  *
      66             :  * (L1)
      67             :  * #/_
      68             :  * If # is in the same counter stage, we just have to yield the name
      69             :  *
      70             :  * If # is in a new counter stage elektraGenOpenFirst already has
      71             :  * yield the map, so we also just have to yield the name
      72             :  *
      73             :  * (L2)
      74             :  * /#
      75             :  * does nothing (even for #/# the array was already done because of
      76             :  * lookahead)
      77             :  *
      78             :  * (L3)
      79             :  * /_
      80             :  * yields the name for the value
      81             :  *
      82             :  * @param g the generator
      83             :  * @param next the key
      84             :  * @retval 0 no value needed afterwards
      85             :  * @retval 1 value is needed
      86             :  */
      87         843 : static int elektraGenOpenValue (yajl_gen g, const Key * next)
      88             : {
      89         843 :         keyNameReverseIterator last = elektraKeyNameGetReverseIterator (next);
      90         843 :         elektraKeyNameReverseNext (&last);
      91             : 
      92         843 :         int valueNeeded = 1;
      93             : 
      94             :         ELEKTRA_LOG_DEBUG ("next: \"%.*s\"", (int) last.size, last.current);
      95             : 
      96         843 :         if (!strcmp (last.current, "###empty_array"))
      97             :         {
      98             :                 ELEKTRA_LOG_DEBUG ("GEN empty array in value");
      99          30 :                 yajl_gen_array_open (g);
     100          30 :                 yajl_gen_array_close (g);
     101          30 :                 valueNeeded = 0;
     102             :         }
     103         813 :         else if (!strcmp (last.current, "___empty_map"))
     104             :         {
     105             :                 ELEKTRA_LOG_DEBUG ("GEN empty map in value");
     106          26 :                 yajl_gen_map_open (g);
     107          26 :                 yajl_gen_map_close (g);
     108          26 :                 valueNeeded = 0;
     109             :         }
     110         787 :         else if (last.current[0] != '#')
     111             :         {
     112             :                 ELEKTRA_LOG_DEBUG ("GEN string (L1,3)");
     113         468 :                 yajl_gen_string (g, (const unsigned char *) last.current, last.size - 1);
     114             :         }
     115             : 
     116         843 :         return valueNeeded;
     117             : }
     118             : 
     119             : 
     120             : /**
     121             :  * @brief Generate the value for the current key
     122             :  *
     123             :  * No auto-guessing takes place, because that can be terrible wrong and
     124             :  * is not reversible. So make sure that all your boolean and numbers
     125             :  * have the proper type in metavalue "type".
     126             :  *
     127             :  * In case of type problems it will be rendered as string but a warning
     128             :  * will be added. Use a type checker to avoid such problems.
     129             :  *
     130             :  * @param g handle to generate to
     131             :  * @param parentKey needed for adding warnings/errors
     132             :  * @param cur the key to generate the value from
     133             :  */
     134         847 : static void elektraGenValue (yajl_gen g, Key * parentKey, const Key * cur)
     135             : {
     136         847 :         if (strcmp (keyName (parentKey), keyName (cur)) && !elektraGenOpenValue (g, cur))
     137             :         {
     138             :                 ELEKTRA_LOG_DEBUG ("Do not yield value");
     139             :                 return;
     140             :         }
     141             : 
     142             :         ELEKTRA_LOG_DEBUG ("GEN value %s for %s", keyString (cur), keyName (cur));
     143             : 
     144         791 :         const Key * type = keyGetMeta (cur, "type");
     145         791 :         if (!type && keyGetValueSize (cur) == 0) // empty binary type is null
     146             :         {
     147          14 :                 yajl_gen_null (g);
     148             :         }
     149        1123 :         else if ((!type && keyGetValueSize (cur) >= 1) || // default is string
     150         346 :                  (!strcmp (keyString (type), "string")))
     151             :         {
     152         431 :                 yajl_gen_string (g, (const unsigned char *) keyString (cur), keyGetValueSize (cur) - 1);
     153             :         }
     154         346 :         else if (!strcmp (keyString (type), "boolean"))
     155             :         {
     156          50 :                 if (!strcmp (keyString (cur), "true"))
     157             :                 {
     158          32 :                         yajl_gen_bool (g, 1);
     159             :                 }
     160          18 :                 else if (!strcmp (keyString (cur), "false"))
     161             :                 {
     162          18 :                         yajl_gen_bool (g, 0);
     163             :                 }
     164             :                 else
     165             :                 {
     166           0 :                         ELEKTRA_ADD_VALIDATION_SEMANTIC_WARNING (parentKey, "Got boolean which is neither true nor false");
     167           0 :                         yajl_gen_string (g, (const unsigned char *) keyString (cur), keyGetValueSize (cur) - 1);
     168             :                 }
     169             :         }
     170         296 :         else if (!strcmp (keyString (type), "double"))
     171             :         {
     172         296 :                 yajl_gen_number (g, keyString (cur), keyGetValueSize (cur) - 1);
     173             :         }
     174             :         else
     175             :         { // unknown or unsupported type, render it as string but add warning
     176           0 :                 ELEKTRA_ADD_VALIDATION_SEMANTIC_WARNINGF (parentKey, "The key %s has unknown type: %s", keyName (cur), keyString (type));
     177           0 :                 yajl_gen_string (g, (const unsigned char *) keyString (cur), keyGetValueSize (cur) - 1);
     178             :         }
     179             : }
     180             : 
     181          68 : int elektraGenEmpty (yajl_gen g, KeySet * returned, Key * parentKey)
     182             : {
     183          68 :         int did_something = 0;
     184             :         // TODO: do all these situations actually occur?
     185          68 :         if (ksGetSize (returned) == 0) // we got nothing..
     186             :         {
     187             :                 ELEKTRA_LOG_DEBUG ("GEN empty map (got nothing)");
     188           0 :                 yajl_gen_map_open (g);
     189           0 :                 yajl_gen_map_close (g);
     190           0 :                 did_something = 1;
     191             :         }
     192          68 :         else if (ksGetSize (returned) == 1) // maybe just parentKey
     193             :         {
     194           5 :                 if (!strcmp (keyName (ksTail (returned)), keyName (parentKey)))
     195             :                 {
     196             :                         ELEKTRA_LOG_DEBUG ("GEN empty map (got parent)");
     197           0 :                         yajl_gen_map_open (g);
     198           0 :                         yajl_gen_map_close (g);
     199           0 :                         did_something = 1;
     200             :                 }
     201             :         }
     202          63 :         else if (ksGetSize (returned) == 2) // maybe just parent+specialkey
     203             :         {
     204           9 :                 Key * toCheck = keyDup (parentKey);
     205             : 
     206           9 :                 keyAddBaseName (toCheck, "###empty_array");
     207           9 :                 if (!strcmp (keyName (ksTail (returned)), keyName (toCheck)))
     208             :                 {
     209             :                         ELEKTRA_LOG_DEBUG ("GEN empty array (got %s)", keyName (ksTail (returned)));
     210           2 :                         yajl_gen_array_open (g);
     211           2 :                         yajl_gen_array_close (g);
     212           2 :                         did_something = 1;
     213             :                 }
     214             : 
     215           9 :                 keySetBaseName (toCheck, "___empty_map");
     216           9 :                 if (!strcmp (keyName (ksTail (returned)), keyName (toCheck)))
     217             :                 {
     218             :                         ELEKTRA_LOG_DEBUG ("GEN empty map (got %s)", keyName (ksTail (returned)));
     219           0 :                         yajl_gen_map_open (g);
     220           0 :                         yajl_gen_map_close (g);
     221           0 :                         did_something = 1;
     222             :                 }
     223           9 :                 keyDel (toCheck);
     224             :         }
     225             : 
     226          68 :         return did_something;
     227             : }
     228             : 
     229          72 : int elektraGenWriteFile (yajl_gen g, Key * parentKey)
     230             : {
     231          72 :         int errnosave = errno;
     232          72 :         FILE * fp = fopen (keyString (parentKey), "w");
     233             : 
     234          72 :         if (!fp)
     235             :         {
     236           0 :                 ELEKTRA_SET_ERROR_SET (parentKey);
     237           0 :                 errno = errnosave;
     238           0 :                 return -1;
     239             :         }
     240             : 
     241             :         const unsigned char * buf;
     242             :         yajl_size_type len;
     243          72 :         yajl_gen_get_buf (g, &buf, &len);
     244          72 :         fwrite (buf, 1, len, fp);
     245          72 :         yajl_gen_clear (g);
     246             : 
     247          72 :         fclose (fp);
     248             : 
     249          72 :         errno = errnosave;
     250          72 :         return 1; /* success */
     251             : }
     252             : 
     253          72 : static void elektraCheckForEmptyArray (KeySet * ks)
     254             : {
     255          72 :         Key * curr = 0;
     256          72 :         ksRewind (ks);
     257             : 
     258        1144 :         while ((curr = ksNext (ks)) != 0)
     259             :         {
     260             :                 ELEKTRA_LOG_DEBUG ("WALK: %s", keyName (curr));
     261        1000 :                 const char * meta = keyString (keyGetMeta (curr, "array"));
     262        1000 :                 if (*meta == '\0')
     263             :                 {
     264          32 :                         cursor_t cursor = ksGetCursor (ks);
     265             : 
     266          32 :                         Key * k = keyNew (keyName (curr), KEY_END);
     267          32 :                         keyAddBaseName (k, "###empty_array");
     268             : 
     269             :                         ELEKTRA_LOG_DEBUG ("Add empty array: %s", keyName (k));
     270             : 
     271          32 :                         ksAppendKey (ks, k);
     272          32 :                         keyDel (k);
     273             : 
     274          32 :                         ksSetCursor (ks, cursor);
     275             :                 }
     276             :         }
     277          72 : }
     278             : 
     279          72 : int elektraYajlSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned, Key * parentKey)
     280             : {
     281             : #if YAJL_MAJOR == 1
     282             :         yajl_gen_config conf = { 1, "    " };
     283             :         yajl_gen g = yajl_gen_alloc (&conf, NULL);
     284             : #else
     285          72 :         yajl_gen g = yajl_gen_alloc (NULL);
     286          72 :         yajl_gen_config (g, yajl_gen_beautify, 1);
     287             : #endif
     288             : 
     289          72 :         elektraCheckForEmptyArray (returned);
     290             : 
     291          76 :         if (ksGetSize (returned) == 1 && !strcmp (keyName (parentKey), keyName (ksHead (returned))) &&
     292           4 :             keyGetValueSize (ksHead (returned)) > 1)
     293             :         {
     294           4 :                 elektraGenValue (g, parentKey, ksHead (returned));
     295           4 :                 int ret = elektraGenWriteFile (g, parentKey);
     296           4 :                 yajl_gen_free (g);
     297           4 :                 return ret;
     298             :         }
     299             : 
     300          68 :         if (elektraGenEmpty (g, returned, parentKey))
     301             :         {
     302           2 :                 int ret = elektraGenWriteFile (g, parentKey);
     303           2 :                 yajl_gen_free (g);
     304           2 :                 return ret;
     305             :         }
     306             : 
     307          66 :         ksRewind (returned);
     308          66 :         Key * cur = elektraNextNotBelow (returned);
     309          66 :         if (!cur)
     310             :         {
     311             :                 // empty config should be handled by resolver
     312             :                 // (e.g. remove file)
     313           0 :                 yajl_gen_free (g);
     314           0 :                 return 0;
     315             :         }
     316             : 
     317             :         ELEKTRA_LOG_DEBUG ("parentKey: %s, cur: %s", keyName (parentKey), keyName (cur));
     318          66 :         elektraGenOpenInitial (g, parentKey, cur);
     319             : 
     320          66 :         Key * next = 0;
     321         909 :         while ((next = elektraNextNotBelow (returned)) != 0)
     322             :         {
     323         777 :                 elektraGenValue (g, parentKey, cur);
     324         777 :                 elektraGenClose (g, cur, next);
     325             : 
     326             :                 ELEKTRA_LOG_DEBUG ("ITERATE: %s next: %s", keyName (cur), keyName (next));
     327         777 :                 elektraGenOpen (g, cur, next);
     328             : 
     329         777 :                 cur = next;
     330             :         }
     331             : 
     332             :         ELEKTRA_LOG_DEBUG ("leaving loop: %s", keyName (cur));
     333             : 
     334          66 :         elektraGenValue (g, parentKey, cur);
     335             : 
     336          66 :         elektraGenCloseFinally (g, cur, parentKey);
     337             : 
     338          66 :         int ret = elektraGenWriteFile (g, parentKey);
     339          66 :         yajl_gen_free (g);
     340             : 
     341          66 :         return ret;
     342             : }

Generated by: LCOV version 1.13