LCOV - code coverage report
Current view: top level - src/plugins/ni/nickel-1.1.0/src - nickel.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 226 242 93.4 %
Date: 2019-09-12 12:28:41 Functions: 34 34 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * Nickel - a library for hierarchical maps and .ini files
       3             :  * One of the Bohr Game Libraries (see chaoslizard.org/devel/bohr)
       4             :  * Copyright (C) 2008 Charles Lindsay.  Some rights reserved; see COPYING.
       5             :  * $Id: nickel.c 339 2008-01-18 19:27:01Z chaz $
       6             :  ******************************************************************************/
       7             : 
       8             : 
       9             : #include "internal.h"
      10             : #include <bohr/ds_hash.h>
      11             : #include <bohr/ni.h>
      12             : 
      13             : #include <kdbmacros.h>
      14             : 
      15             : #include <assert.h>
      16             : #include <ctype.h>
      17             : #include <inttypes.h>
      18             : #include <stdarg.h>
      19             : #include <stddef.h>
      20             : #include <stdint.h>
      21             : #include <stdio.h>
      22             : #include <stdlib.h>
      23             : #include <string.h>
      24             : 
      25             : 
      26             : // How many child buckets to pre-allocate for each node.  32 seems appropriate
      27             : // because each bucket is only a pointer in size, and resizing the table is
      28             : // time-consuming.
      29             : #define INITIAL_BUCKETS 32
      30             : 
      31             : // How big to initialize the buffer for reading values in elektraNi_ReadStream().
      32             : #define INITIAL_VALUE_BUFFER 1024
      33             : 
      34             : 
      35             : // A node in our Ni tree.
      36             : struct elektraNi_node_struct
      37             : {
      38             :         struct elektraNi_node_struct * root;   // root node in this tree (ALWAYS set for valid nodes)
      39             :         struct elektraNi_node_struct * parent; // the immediate parent of this node (set for all except root)
      40             : 
      41             :         char name[elektraNi_KEY_SIZE]; // this node's name (set for all except root)
      42             :         int name_len;                  // strlen of name
      43             :         Ds_hash_t hash;                // the hash value of name and thus this node
      44             : 
      45             :         Ds_str value; // this node's value (only set for nodes that have a value)
      46             :         int modified; // whether this value has been "modified", which the application can use however they want
      47             : 
      48             :         Ds_hash_table children; // a hash table of children
      49             : };
      50             : #define NODE_STRUCT_INIT                                                                                                                   \
      51             :         {                                                                                                                                  \
      52             :                 NULL, NULL, { '\0' }, 0, Ds_HASH_C (0), Ds_STR_INIT, 0, Ds_HASH_TABLE_INIT                                                 \
      53             :         }
      54             : 
      55             : 
      56             : // Returns a Ds_hash_entry's item as a elektraNi_node.
      57             : #define GetItem(e) ((elektraNi_node) ((e)->item))
      58             : 
      59             : // Returns the Ds_hash_entry the node belongs to.
      60             : #define GetEntry(n) ((Ds_hash_entry *) ((unsigned char *) (n) -offsetof (Ds_hash_entry, item)))
      61             : 
      62             : // Inits the node and adds it as a child of the parent.
      63             : static elektraNi_node AddNode (elektraNi_node restrict n, elektraNi_node restrict parent, const char * restrict name, int name_len,
      64             :                                Ds_hash_t hash);
      65             : 
      66             : // Initializes internals of a node.
      67             : static int InitNode (elektraNi_node restrict n, elektraNi_node restrict parent);
      68             : 
      69             : // Frees internals of a node.
      70             : static void FreeNode (elektraNi_node restrict n);
      71             : 
      72             : // Frees internals of this node and all its children.
      73             : static void RecursiveFree (elektraNi_node restrict n);
      74             : 
      75             : // Sets the modified state for this node and all its children.
      76             : static void RecursiveSetModified (elektraNi_node restrict n, int modified);
      77             : 
      78             : // Recursively outputs to the stream.
      79             : static int RecursiveWrite (elektraNi_node restrict n, FILE * restrict stream, int modified_only, int level);
      80             : 
      81             : // Compares two hash entries.
      82             : static int Compare (const void * restrict key, size_t key_size, const void * restrict item, size_t item_size);
      83             : 
      84             : 
      85             : /* Returns the version of Ni this library was compiled with.  Compare this
      86             :  * value to elektraNi_VERSION to see if the header matches.
      87             :  */
      88           2 : elektraNi_PUBLIC uint32_t elektraNi_GetVersion (void)
      89             : {
      90           2 :         return elektraNi_HEADER_VERSION;
      91             : }
      92             : 
      93             : /* Allocates an entirely new node, not connected to any others, and returns it.
      94             :  * This new node is the root of ... well, so far, just itself, but anything you
      95             :  * add to it will be its children.  Returns NULL if it fails.  Note that to
      96             :  * allocate a node that will be a child of another node, you must use
      97             :  * elektraNi_GetChild() instead of this function; this only allocates entirely new
      98             :  * trees.
      99             :  */
     100         186 : elektraNi_PUBLIC elektraNi_node elektraNi_New (void)
     101             : {
     102             :         elektraNi_node n;
     103             : 
     104         186 :         if ((n = (elektraNi_node) malloc (sizeof (struct elektraNi_node_struct))) != NULL)
     105             :         {
     106         186 :                 if (!InitNode (n, NULL))
     107             :                 {
     108           0 :                         elektraFree (n);
     109           0 :                         n = NULL;
     110             :                 }
     111             :         }
     112             : 
     113         186 :         return n;
     114             : }
     115             : 
     116             : /* Frees a node and any of the node's children, and their children, etc.  It
     117             :  * also checks if it has a parent and severs itself as a branch, so the entire
     118             :  * tree is kept synchronized.
     119             :  */
     120         186 : elektraNi_PUBLIC void elektraNi_Free (elektraNi_node restrict n)
     121             : {
     122         186 :         if (n)
     123             :         {
     124         186 :                 RecursiveFree (n);
     125             : 
     126             :                 // if it was root, it was created with malloc, so free it
     127         186 :                 if (n == n->root)
     128             :                 {
     129         186 :                         elektraFree (n);
     130             :                 }
     131             :                 else
     132             :                 {
     133             :                         // otherwise, we just remove it from its parent's hash table
     134             :                         assert (n->parent != NULL);
     135           0 :                         if (!Ds_RemoveHashEntry (&n->parent->children, GetEntry (n)))
     136             :                                 assert (!"Ds_RemoveHashEntry() should never fail in this case!");
     137             :                         // more descriptive than assert(0)
     138             :                 }
     139             :         }
     140         186 : }
     141             : 
     142             : /* Returns the name string of a node, or NULL if you pass an invalid node or a
     143             :  * root node (roots can't have names).  If len_out is non-NULL, it sticks the
     144             :  * returned string's length in len_out.  Note that all nodes have a name except
     145             :  * the root--but names can be "".
     146             :  */
     147        2059 : elektraNi_PUBLIC const char * elektraNi_GetName (elektraNi_node restrict n, int * restrict len_out)
     148             : {
     149        2059 :         const char * name = NULL;
     150        2059 :         int len = 0;
     151             : 
     152        2059 :         if (n && n != n->root)
     153             :         {
     154        2057 :                 name = n->name;
     155        2057 :                 len = n->name_len;
     156             :         }
     157             : 
     158        2059 :         if (len_out)
     159             :         {
     160         690 :                 *len_out = len;
     161             :         }
     162        2059 :         return name;
     163             : }
     164             : 
     165             : /* Returns the root of the tree the node belongs to.  You can check if a node
     166             :  * is a root node if n == elektraNi_GetRoot(n) is nonzero.  Returns NULL if you pass
     167             :  * an invalid node.
     168             :  */
     169           2 : elektraNi_PUBLIC elektraNi_node elektraNi_GetRoot (elektraNi_node restrict n)
     170             : {
     171           2 :         return (n ? n->root : NULL);
     172             : }
     173             : 
     174             : /* Returns the parent node of a node, or NULL if you pass an invalid node or a
     175             :  * root node (as they have no parent).
     176             :  */
     177         285 : elektraNi_PUBLIC elektraNi_node elektraNi_GetParent (elektraNi_node restrict n)
     178             : {
     179         285 :         return (n ? n->parent : NULL);
     180             : }
     181             : 
     182             : /* Returns the number of children a node has, or 0 if you pass an invalid node.
     183             :  */
     184         539 : elektraNi_PUBLIC int elektraNi_GetNumChildren (elektraNi_node restrict n)
     185             : {
     186         539 :         return (n ? n->children.num : 0);
     187             : }
     188             : 
     189             : /* Useful for enumerating children--pass NULL as child to retrieve the node's
     190             :  * first child, then pass the previous return value as child to get the next,
     191             :  * etc.  Returns NULL if there's an error or there are no more children.
     192             :  */
     193        3321 : elektraNi_PUBLIC elektraNi_node elektraNi_GetNextChild (elektraNi_node restrict n, elektraNi_node restrict child)
     194             : {
     195        3321 :         elektraNi_node next = NULL;
     196             :         Ds_hash_entry * e;
     197             : 
     198        3321 :         if (n)
     199             :         {
     200        6642 :                 if ((e = Ds_NextHashEntry (&n->children, (child ? GetEntry (child) : NULL))) != NULL) next = GetItem (e);
     201             :         }
     202             : 
     203        3321 :         return next;
     204             : }
     205             : 
     206             : /* Returns the named child node of the node.  Specify the length of the name in
     207             :  * name_len, or use a negative number to have it calculated for you using
     208             :  * strlen().  Note that a NULL name is treated as "".  If add_if_new is
     209             :  * nonzero, if the named node is not found, it will be allocated as a child of
     210             :  * the node and returned.  You can see whether it was found or added if you
     211             :  * specify a non-NULL added_out.  Returns NULL if the node wasn't found AND
     212             :  * either add_if_new was 0 or it failed to allocate a new node (in either case,
     213             :  * added_out will be 0).
     214             :  */
     215        2357 : elektraNi_PUBLIC elektraNi_node elektraNi_GetChild (elektraNi_node restrict n, const char * restrict name, int name_len, int add_if_new,
     216             :                                                     int * restrict added_out)
     217             : {
     218        2357 :         elektraNi_node child = NULL;
     219             :         struct elektraNi_node_struct c;
     220             :         Ds_hash_entry * e;
     221        2357 :         int added = 0;
     222             :         Ds_hash_t hash;
     223             : 
     224        2357 :         if (n)
     225             :         {
     226        2357 :                 if (!name)
     227             :                 {
     228          13 :                         name = "";
     229             :                 }
     230        2357 :                 if (name_len < 0)
     231             :                 {
     232         152 :                         name_len = strlen (name);
     233             :                 }
     234        2357 :                 if (name_len > elektraNi_KEY_SIZE - 1) name_len = elektraNi_KEY_SIZE - 1;
     235             : 
     236        2357 :                 hash = Hash (name, (size_t) name_len, 0xbadc0de5);
     237             : 
     238        4714 :                 if ((e = Ds_SearchHashTable (&n->children, name, name_len, hash, Compare)) != NULL)
     239         453 :                         child = GetItem (e);
     240        1904 :                 else if (add_if_new)
     241             :                 {
     242        1904 :                         if ((child = AddNode (&c, n, name, name_len, hash)) != NULL) added = 1;
     243             :                 }
     244             :         }
     245             : 
     246        2357 :         if (added_out)
     247             :         {
     248           0 :                 *added_out = added;
     249             :         }
     250        2357 :         return child;
     251             : }
     252             : 
     253             : /* Returns the modified state of a node.  When nodes are created, they are "not
     254             :  * modified".  As soon as you call a elektraNi_SetValue() (or elektraNi_ValuePrint())
     255             :  * function, its modified state changes to "modified".  Use this to check
     256             :  * whether it's modified or not.  You can tell elektraNi_WriteFile() or -Stream() to
     257             :  * only write modified values--this is useful for having a global options file
     258             :  * with a local override file (you'd read the global options in, set all nodes
     259             :  * to "not modified", then read in the local options over top of it, and any
     260             :  * values the local file set will then be "modified", and when you write it
     261             :  * out, you only get the options set by the override file or ones you set since
     262             :  * it was loaded).
     263             :  */
     264           6 : elektraNi_PUBLIC int elektraNi_GetModified (elektraNi_node restrict n)
     265             : {
     266           6 :         return (n ? n->modified : 0);
     267             : }
     268             : 
     269             : /* Explicitly sets the modified state for a node, and if recurse is nonzero,
     270             :  * all the node's children and their children, etc.  See the note in the above
     271             :  * function how this is useful.
     272             :  */
     273           6 : elektraNi_PUBLIC void elektraNi_SetModified (elektraNi_node restrict n, int modified, int recurse)
     274             : {
     275           6 :         if (n)
     276             :         {
     277           6 :                 if (!recurse)
     278             :                 {
     279           4 :                         n->modified = modified;
     280             :                 }
     281             :                 else
     282             :                 {
     283           2 :                         RecursiveSetModified (n, modified);
     284             :                 }
     285             :         }
     286           6 : }
     287             : 
     288             : /* Returns a node's value.  Any node except a root node can have a value, but
     289             :  * not all of them do.  Until a node's value is set with a elektraNi_SetValue() (or
     290             :  * elektraNi_PrintValue()) function, it does NOT have a value.  Returns the value as a
     291             :  * string, or NULL if the function doesn't have a value or you pass an invalid
     292             :  * or root node.  If you care about the length of the value string, pass a non-
     293             :  * NULL len_out and its length is returned there.
     294             :  */
     295        1928 : elektraNi_PUBLIC const char * elektraNi_GetValue (elektraNi_node restrict n, int * restrict len_out)
     296             : {
     297        1928 :         const char * value = NULL;
     298        1928 :         int len = 0;
     299             : 
     300        1928 :         if (n && n != n->root)
     301             :         {
     302        1926 :                 value = n->value.str;
     303        1926 :                 len = n->value.len;
     304             :         }
     305             : 
     306        1928 :         if (len_out)
     307             :         {
     308         545 :                 *len_out = len;
     309             :         }
     310        1928 :         return value;
     311             : }
     312             : 
     313             : /* Returns a node's value interpreted as a long, or 0 if the node doesn't have
     314             :  * a value (see elektraNi_GetValue()).  Note that it uses strtol()'s base detection so
     315             :  * strings starting with 0 are considered octal, and 0x are considered hex.
     316             :  */
     317           4 : elektraNi_PUBLIC long elektraNi_GetValueInt (elektraNi_node restrict n)
     318             : {
     319           4 :         long i = 0L;
     320             :         const char * v;
     321             : 
     322           4 :         if ((v = elektraNi_GetValue (n, NULL)) != NULL)
     323             :         {
     324           4 :                 i = strtol (v, NULL, 0);
     325             :         }
     326             : 
     327           4 :         return i;
     328             : }
     329             : 
     330             : /* Returns the node's value interpreted as a double, or 0.0 if the node doesn't
     331             :  * have a value (see elektraNi_GetValue()).
     332             :  */
     333           4 : elektraNi_PUBLIC double elektraNi_GetValueFloat (elektraNi_node restrict n)
     334             : {
     335           4 :         double d = 0.0;
     336             :         const char * v;
     337             : 
     338           4 :         if ((v = elektraNi_GetValue (n, NULL)) != NULL)
     339             :         {
     340           4 :                 d = strtod (v, NULL);
     341             :         }
     342             : 
     343           4 :         return d;
     344             : }
     345             : 
     346             : /* Returns the node's value interpreted as a boolean integer (0/1), or 0 if the
     347             :  * node doesn't have a value (see elektraNi_GetValue()).  The following strings are
     348             :  * considered "true" and have a nonzero return value from this function: any
     349             :  * string starting with T or Y, the string "on", (case is ignored in all those
     350             :  * cases), or any nonzero integer.  Everything else is considered "false" and
     351             :  * will result in a 0 return value.
     352             :  */
     353           4 : elektraNi_PUBLIC int elektraNi_GetValueBool (elektraNi_node restrict n)
     354             : {
     355           4 :         int b = 0;
     356             :         const char * v;
     357             :         int len;
     358             : 
     359           4 :         if ((v = elektraNi_GetValue (n, &len)) != NULL)
     360             :         {
     361           4 :                 if (*v == 'T' || *v == 't' || *v == 'Y' || *v == 'y' || strtol (v, NULL, 0) ||
     362           0 :                     (len == 2 && (*v == 'o' || *v == 'O') && (*(v + 1) == 'n' || *(v + 1) == 'N')))
     363             :                 {
     364             :                         b = 1;
     365             :                 }
     366             :         }
     367             : 
     368           4 :         return b;
     369             : }
     370             : 
     371             : /* Calls vsscanf() on the node's value string directly.  format is the scanf()-
     372             :  * formatted argument string, and any arguments for scanf() are passed after
     373             :  * format.  Returns what scanf() returns: the number of translated items.
     374             :  */
     375           6 : elektraNi_PUBLIC int elektraNi_ValueScan (elektraNi_node restrict n, const char * restrict format, ...)
     376             : {
     377             :         int rc;
     378             :         va_list args;
     379             : 
     380           6 :         va_start (args, format);
     381           6 :         rc = elektraNi_ValueVScan (n, format, args);
     382           6 :         va_end (args);
     383             : 
     384           6 :         return rc;
     385             : }
     386             : 
     387             : /* Same as above, except you pass a va_list instead of the args directly.
     388             :  */
     389           6 : elektraNi_PUBLIC int elektraNi_ValueVScan (elektraNi_node restrict n, const char * restrict format, va_list args)
     390             : {
     391           6 :         int items = 0;
     392             :         const char * v;
     393             : 
     394           6 :         if ((v = elektraNi_GetValue (n, NULL)) != NULL)
     395             :         {
     396           6 :                 items = vsscanf (v, format, args);
     397             :         }
     398             : 
     399           6 :         return items;
     400             : }
     401             : 
     402             : /* Sets or removes a node's value.  If value is non-NULL, the value is set to
     403             :  * that string (which can be any length--specify its length in value_len, or
     404             :  * pass a negative value in value_len and it'll be calculated automatically
     405             :  * using strlen()).  If value is NULL (value_len is ignored in this case), its
     406             :  * value is removed--subsequent calls to elektraNi_GetValue() will return NULL (until
     407             :  * you set its value to something non-NULL, anyway).  Returns the length of
     408             :  * value, either as passed or calculated, or -1 if it fails, or 0 if you're
     409             :  * removing a value (so a negative return value always indicates error).  If
     410             :  * the value setting or removing succeeds, the node's modified state is set to
     411             :  * 1.  If setting the value fails, the contents of the value will NOT have
     412             :  * changed (and its modified state won't have changed either).  Note that for
     413             :  * setting the value, the value string you pass need not persist after the
     414             :  * call--its contents are copied.
     415             :  */
     416        1904 : elektraNi_PUBLIC int elektraNi_SetValue (elektraNi_node restrict n, const char * restrict value, int value_len)
     417             : {
     418        1904 :         int len = -1;
     419             : 
     420             :         // if it's a valid node and this isn't the root node (root can't have a
     421             :         // value)
     422        1904 :         if (n && n != n->root)
     423             :         {
     424             :                 // if they're specifying a new value
     425        1902 :                 if (value)
     426             :                 {
     427             :                         int old_len;
     428             : 
     429        1902 :                         old_len = n->value.len;
     430        1902 :                         n->value.len = 0; // don't concatenate, but copy
     431             : 
     432             :                         // Ds_StrCat() handles name_len being negative, so we don't need to fix
     433             :                         // it here
     434        1902 :                         if ((len = Ds_StrCat (&n->value, value, value_len)) < 0)
     435             :                         {
     436             :                                 // Ds_StrCat() returns a negative number if it fails, so we
     437             :                                 // don't need to do anything to len here
     438             : 
     439           0 :                                 n->value.len = old_len;
     440             :                         }
     441             :                         else
     442        1902 :                                 n->modified = 1;
     443             :                 }
     444             :                 else // they're deleting the value
     445             :                 {
     446             :                         // so we free it and re-init it to NULL
     447           0 :                         Ds_FreeStr (&n->value);
     448             :                         // the string will have a null value now, and can still be copied/
     449             :                         // printed to just fine
     450             : 
     451           0 :                         n->modified = 1;
     452           0 :                         len = 0;
     453             :                 }
     454             :         }
     455             : 
     456        1904 :         return len;
     457             : }
     458             : 
     459             : /* Sets a node's value to the value of a long.  Semantics are similar to those
     460             :  * of elektraNi_SetValue(), except you can't remove a node's value with this function.
     461             :  */
     462           2 : elektraNi_PUBLIC int elektraNi_SetValueInt (elektraNi_node restrict n, long value)
     463             : {
     464           2 :         return elektraNi_ValuePrint (n, "%ld", value);
     465             : }
     466             : 
     467             : /* Sets a node's value to the value of a double.  Semantics are similar to
     468             :  * those of elektraNi_SetValue(), except you can't remove a node's value with this
     469             :  * function.
     470             :  */
     471           2 : elektraNi_PUBLIC int elektraNi_SetValueFloat (elektraNi_node restrict n, double value)
     472             : {
     473           2 :         return elektraNi_ValuePrint (n, "%.17g", value);
     474             : }
     475             : 
     476             : /* Sets a node's value to "true" or "false" based on a boolean integer.
     477             :  * Semantics are similar to those of elektraNi_SetValue(), except you can't remove a
     478             :  * node's value with this function.
     479             :  */
     480           2 : elektraNi_PUBLIC int elektraNi_SetValueBool (elektraNi_node restrict n, int value)
     481             : {
     482           2 :         return elektraNi_SetValue (n, (value ? "true" : "false"), (value ? 4 : 5));
     483             : }
     484             : 
     485             : /* Uses printf() formatting to set the node's value.  You can't remove a node's
     486             :  * value with this function.  format is the printf()-formatted string, and any
     487             :  * arguments are passed after it.  Returns the resulting string length, or -1
     488             :  * if an error occurs (which may be as mundane as you passing an invalid or
     489             :  * root node).  If this function fails, unfortunately the contents of the value
     490             :  * MAY have changed due to printf() having complicated internal workings.  If
     491             :  * it fails, though, the modified state won't have changed, so you won't write
     492             :  * garbage if you're only writing modified values.  I don't know what else to
     493             :  * do, really.
     494             :  */
     495           8 : elektraNi_PUBLIC int elektraNi_ValuePrint (elektraNi_node restrict n, const char * restrict format, ...)
     496             : {
     497             :         int rc;
     498             :         va_list args;
     499             : 
     500           8 :         va_start (args, format);
     501           8 :         rc = elektraNi_ValueVPrint (n, format, args);
     502           8 :         va_end (args);
     503             : 
     504           8 :         return rc;
     505             : }
     506             : 
     507             : /* Same as above, except it expects a va_list instead of the args passed after
     508             :  * the format string.
     509             :  */
     510           8 : elektraNi_PUBLIC int elektraNi_ValueVPrint (elektraNi_node restrict n, const char * restrict format, va_list args)
     511             : {
     512           8 :         int len = -1;
     513             : 
     514             :         // if it's a valid node and this isn't the root node (root can't have a
     515             :         // value)
     516           8 :         if (n && n != n->root)
     517             :         {
     518             :                 int old_len;
     519           8 :                 old_len = n->value.len;
     520           8 :                 n->value.len = 0; // don't concatenate, but copy
     521             : 
     522             :                 // Ds_StrCatVPrint() handles format being NULL, so we don't need to fix it
     523             :                 // here
     524           8 :                 if ((len = Ds_StrCatVPrint (&n->value, format, args)) < 0)
     525             :                 {
     526             :                         // Ds_StrCatVPrint() returns a negative number if it fails, so we
     527             :                         // don't need to do anything to len here
     528             : 
     529           0 :                         n->value.len = old_len;
     530             :                 }
     531             :                 else
     532           8 :                         n->modified = 1;
     533             :         }
     534             : 
     535           8 :         return len;
     536             : }
     537             : 
     538             : /* Writes the contents of the tree starting at the node out to a file, in a
     539             :  * format that is parsable by elektraNi_ReadFile() or -Stream(), and roughly
     540             :  * compatible with .ini files.  Note that you can pass any node of a tree to
     541             :  * this function--only its children and downward are output.  If you pass
     542             :  * modified_only as nonzero, values are only output if the node's modified
     543             :  * state is true (either way, you must manually set the nodes' modified states
     544             :  * to 0 after calling this function, if you want to keep track of it that way).
     545             :  * Returns 0 on error, or nonzero on success.  The file is opened with
     546             :  * fopen(filename, "w"), so its contents will be erased.
     547             :  */
     548          69 : elektraNi_PUBLIC int elektraNi_WriteFile (elektraNi_node restrict n, const char * restrict filename, int modified_only)
     549             : {
     550          69 :         int rc = 0;
     551          69 :         FILE * fp = NULL;
     552             : 
     553          69 :         if (filename)
     554             :         {
     555          69 :                 if ((fp = fopen (filename, "w")) != NULL)
     556             :                 {
     557          69 :                         rc = elektraNi_WriteStream (n, fp, modified_only);
     558          69 :                         fclose (fp);
     559             :                 }
     560             :         }
     561             : 
     562          69 :         return rc;
     563             : }
     564             : 
     565             : /* Same as above, except instead of a filename, you can pass an already-open
     566             :  * file or a stream (like stdout).  The file must be writable, but need not be
     567             :  * seekable.
     568             :  */
     569          75 : elektraNi_PUBLIC int elektraNi_WriteStream (elektraNi_node restrict n, FILE * restrict stream, int modified_only)
     570             : {
     571          75 :         int success = 0;
     572             :         do
     573             :         {
     574          75 :                 if (!n || !stream) break;
     575             : 
     576          75 :                 if (fprintf (stream,
     577             :                              ";Ni1\n"
     578             :                              "; Generated by Nickel Plugin using Elektra (see libelektra.org).\n\n") < 0)
     579             :                 {
     580             :                         break;
     581             :                 }
     582             : 
     583          75 :                 if (!RecursiveWrite (n, stream, modified_only, 0)) break;
     584             : 
     585          75 :                 success = 1;
     586             :         } while (0);
     587             : 
     588          75 :         return success;
     589             : }
     590             : 
     591             : /* Reads the contents of the file into the node and its children.  The node is
     592             :  * treated as the root of the resulting tree, but need not actually be the root
     593             :  * of the tree.  Any existing values in the tree are overwritten by the ones
     594             :  * from the file, or are created if they don't exist.  Returns 0 if it fails
     595             :  * (the contents of the node's children are undefined in this case), or nonzero
     596             :  * if it succeeds (invalid lines in a file won't make the function fail--
     597             :  * they'll just be skipped).  If fold_case is nonzero, all node names will be
     598             :  * converted to lowercase as they're read in.  Since "Name" and "name" are
     599             :  * different names, this makes the files less strict with case.
     600             :  */
     601         101 : elektraNi_PUBLIC int elektraNi_ReadFile (elektraNi_node restrict n, const char * restrict filename, int fold_case)
     602             : {
     603         101 :         int rc = 0;
     604         101 :         FILE * fp = NULL;
     605             : 
     606         101 :         if (filename)
     607             :         {
     608         101 :                 if ((fp = fopen (filename, "r")) != NULL)
     609             :                 {
     610         101 :                         rc = elektraNi_ReadStream (n, fp, fold_case);
     611         101 :                         fclose (fp);
     612             :                 }
     613             :         }
     614             : 
     615         101 :         return rc;
     616             : }
     617             : 
     618             : /* Same as above, except instead of a filename, you pass an already-open file
     619             :  * or stream (like stdin).  The file must be readable, but need not be
     620             :  * seekable.
     621             :  */
     622         103 : elektraNi_PUBLIC int elektraNi_ReadStream (elektraNi_node restrict n, FILE * restrict stream, int fold_case)
     623             : {
     624         103 :         file_buf fb = FILE_BUF_INIT;             // the file buffer we're reading
     625         103 :         char key[elektraNi_KEY_SIZE] = { '\0' }; // section/key name for GetNextIdentifier
     626             :         int key_len;                             // length of string in 'key' buffer
     627             :         int key_level;                           // how many ['s were in front of key, if section
     628         103 :         int cur_level = 0;                       // where we currently are in the tree
     629         103 :         Ds_str value = Ds_STR_INIT;              // value holder string
     630             :         int result;                              // the result of internal operations
     631             :         elektraNi_node child;                    // a child
     632             :         int i;
     633             : 
     634         103 :         int success = 0;
     635             :         do
     636             :         {
     637         103 :                 if (!n || !stream) break;
     638             : 
     639         103 :                 if (!InitFileBuf (&fb, stream)) break;
     640         103 :                 if (!Ds_InitStr (&value, INITIAL_VALUE_BUFFER)) break;
     641             : 
     642             :                 // do this until eof
     643        1795 :                 while ((result = GetNextIdentifier (&fb, key, &key_len, &key_level)) != 0)
     644             :                 {
     645        1692 :                         if (result < 0) break;
     646             : 
     647        1692 :                         if (fold_case)
     648             :                         {
     649             :                                 // FIXME: this breaks valid UTF-8 and is locale-dependent...
     650             : 
     651           0 :                                 for (i = 0; i < key_len; ++i)
     652           0 :                                         key[i] = tolower (key[i]);
     653             :                         }
     654             : 
     655        1692 :                         if (result == 1) // if section name
     656             :                         {
     657             :                                 // if key_level is more deeply nested than we are currently, by more
     658             :                                 // than 1 level
     659         377 :                                 while (key_level - cur_level > 1)
     660             :                                 {
     661             :                                         // get or add nameless children, as necessary
     662           0 :                                         if (!(n = elektraNi_GetChild (n, "", 0, 1, NULL))) break;
     663           0 :                                         ++cur_level;
     664             :                                 }
     665             :                                 // if key_level is less deeply nested than we are currently, by more
     666             :                                 // than 1
     667         660 :                                 while (key_level - cur_level < 1)
     668             :                                 {
     669         283 :                                         if (!(n = elektraNi_GetParent (n))) break;
     670         283 :                                         --cur_level;
     671             :                                 }
     672             : 
     673         377 :                                 if (key_level - cur_level != 1) result = -1;
     674             :                         }
     675        1692 :                         if (result < 0) break;
     676             : 
     677             :                         // get/add the child
     678        1692 :                         if (!(child = elektraNi_GetChild (n, key, key_len, 1, NULL)))
     679             :                         {
     680             :                                 result = -1;
     681             :                                 break;
     682             :                         }
     683             : 
     684        1692 :                         if (result == 1) // if it was a section
     685             :                         {
     686         377 :                                 n = child;   // we've got to start from there next time
     687         377 :                                 ++cur_level; // and say we're a level deeper
     688             :                         }
     689             :                         else // it was a key=value pair
     690             :                         {
     691             :                                 // then get the upcoming value into it
     692        1315 :                                 if (!GetValue (&fb, &value))
     693             :                                 {
     694             :                                         result = -1;
     695             :                                         break;
     696             :                                 }
     697             : 
     698             :                                 // set the new child's value
     699        1315 :                                 if (elektraNi_SetValue (child, value.str, value.len) < 0)
     700             :                                 {
     701             :                                         result = -1;
     702             :                                         break;
     703             :                                 }
     704             :                         }
     705             :                 }
     706         103 :                 if (result < 0) // if we dipped out early
     707             :                         break;
     708             : 
     709         103 :                 success = 1;
     710             :         } while (0);
     711             : 
     712         103 :         FreeFileBuf (&fb);
     713         103 :         Ds_FreeStr (&value);
     714             : 
     715         103 :         return success;
     716             : }
     717             : 
     718             : /* Initializes the node pointed to by n, makes sure there's space in the
     719             :  * parent's table, and adds the node as its child.  Returns NULL if it fails,
     720             :  * or the added node if it succeeds.
     721             :  */
     722        1904 : static elektraNi_node AddNode (elektraNi_node restrict n, elektraNi_node restrict parent, const char * restrict name, int name_len,
     723             :                                Ds_hash_t hash)
     724             : {
     725        1904 :         int success = 0;
     726        1904 :         elektraNi_node child = NULL;
     727        1904 :         Ds_hash_entry * e = NULL;
     728             : 
     729             :         assert (n != NULL);
     730             :         assert (parent != NULL);
     731             :         assert (name_len < elektraNi_KEY_SIZE);
     732             : 
     733             :         do
     734             :         {
     735             :                 // init the node
     736        1904 :                 if (!InitNode (n, parent)) break;
     737             : 
     738             :                 // give it a name
     739        1904 :                 memcpy (n->name, name, name_len * sizeof (char));
     740        1904 :                 n->name[n->name_len = name_len] = '\0';
     741             : 
     742             :                 // check for space (requires 25% free space in the table) and grow the
     743             :                 // table by a factor of 2 if it's too small
     744        1908 :                 if (parent->children.num >= ((parent->children.cap >> 2) + (parent->children.cap >> 1)) &&
     745           4 :                     !Ds_ResizeHashTable (&parent->children, parent->children.cap << 1))
     746             :                         break;
     747             : 
     748             :                 // insert it
     749        1904 :                 if (!(e = Ds_InsertHashItem (&parent->children, n, sizeof (struct elektraNi_node_struct), hash))) break;
     750        1904 :                 child = GetItem (e); // get the inserted item
     751             : 
     752        1904 :                 success = 1;
     753             :         } while (0);
     754             : 
     755        1904 :         if (!success)
     756             :         {
     757           0 :                 if (e) Ds_RemoveHashEntry (&parent->children, e);
     758           0 :                 FreeNode (n);
     759             :         }
     760             : 
     761        1904 :         return child;
     762             : }
     763             : 
     764             : /* Initializes the contents of the node.
     765             :  */
     766        1904 : static int InitNode (elektraNi_node restrict n, elektraNi_node restrict parent)
     767             : {
     768             :         assert (n);
     769             : 
     770        2090 :         *n = (struct elektraNi_node_struct) NODE_STRUCT_INIT;
     771             : 
     772        2090 :         n->root = (parent ? parent->root : n);
     773        1904 :         n->parent = parent;
     774             : 
     775             :         // make space for children
     776        2090 :         return Ds_InitHashTable (&n->children, INITIAL_BUCKETS);
     777             : }
     778             : 
     779             : /* Frees the contents of the node.
     780             :  */
     781        2090 : static void FreeNode (elektraNi_node restrict n)
     782             : {
     783             :         assert (n != NULL);
     784             : 
     785        4180 :         Ds_FreeStr (&n->value);           // free its value
     786        2090 :         Ds_FreeHashTable (&n->children); // free its array of children
     787        2090 : }
     788             : 
     789             : /* Calls the above on the node and all its children.
     790             :  */
     791        2090 : static void RecursiveFree (elektraNi_node restrict n)
     792             : {
     793        2090 :         Ds_hash_entry * e = NULL;
     794             : 
     795             :         assert (n != NULL);
     796             : 
     797       10078 :         while ((e = Ds_NextHashEntry (&n->children, e)) != NULL)
     798        1904 :                 RecursiveFree (GetItem (e)); // free it
     799             : 
     800        2090 :         FreeNode (n);
     801        2090 : }
     802             : 
     803             : /* Sets a node's modified state recursively.
     804             :  */
     805          10 : static void RecursiveSetModified (elektraNi_node restrict n, int modified)
     806             : {
     807          10 :         Ds_hash_entry * e = NULL;
     808             : 
     809             :         assert (n != NULL);
     810             : 
     811          46 :         while ((e = Ds_NextHashEntry (&n->children, e)) != NULL)
     812           8 :                 RecursiveSetModified (GetItem (e), modified); // set that shit
     813             : 
     814          10 :         n->modified = modified;
     815          10 : }
     816             : 
     817             : /* Writes all the node's children that have values (and maybe are modified)
     818             :  * out, then writes section names and calls itself recursively for any children
     819             :  * that have children.  Returns 0 if it fails, nonzero on success.
     820             :  */
     821         234 : static int RecursiveWrite (elektraNi_node restrict n, FILE * restrict stream, int modified_only, int level)
     822             : {
     823             :         elektraNi_node child;
     824             :         const char * name;
     825             :         int name_len;
     826             :         const char * value;
     827             :         int value_len;
     828         234 :         int success = 0;
     829             : 
     830             :         assert (n != NULL);
     831             : 
     832             :         do
     833             :         {
     834             :                 // loop through all children
     835         234 :                 child = NULL;
     836         999 :                 while ((child = elektraNi_GetNextChild (n, child)) != NULL)
     837             :                 {
     838             :                         // get its name
     839         531 :                         name = elektraNi_GetName (child, &name_len);
     840             :                         assert (name != NULL);
     841             : 
     842             :                         // get its value and only do anything if it's modified or we're writing
     843             :                         // all children
     844         531 :                         if ((value = elektraNi_GetValue (child, &value_len)) != NULL && (!modified_only || elektraNi_GetModified (child)))
     845             :                         {
     846             :                                 // put the actual key/value pair
     847         521 :                                 if (!PutEntry (stream, name, name_len, value, value_len, level + 1)) break;
     848             :                         }
     849             :                 }
     850         234 :                 if (child) // if we broke out early
     851             :                         break;
     852             : 
     853             :                 // go through all children again
     854             :                 child = NULL;
     855         765 :                 while ((child = elektraNi_GetNextChild (n, child)) != NULL)
     856             :                 {
     857             :                         // if this child has children
     858         531 :                         if (elektraNi_GetNumChildren (child) > 0)
     859             :                         {
     860             :                                 // get the child's name
     861         159 :                                 name = elektraNi_GetName (child, &name_len);
     862             :                                 assert (name != NULL);
     863             : 
     864             :                                 // put it as a section name
     865         159 :                                 if (!PutSection (stream, name, name_len, level + 1)) break;
     866             : 
     867             :                                 // recurse
     868         159 :                                 if (!RecursiveWrite (child, stream, modified_only, level + 1)) break;
     869             :                         }
     870             :                 }
     871         234 :                 if (child) break;
     872             : 
     873         234 :                 success = 1;
     874             :         } while (0);
     875             : 
     876         234 :         return success;
     877             : }
     878             : 
     879             : /* Compares a key with a elektraNi_node_struct's name for Ds_SearchHashTable().
     880             :  */
     881         227 : static int Compare (const void * restrict key, size_t key_size, const void * restrict item, size_t item_size ELEKTRA_UNUSED)
     882             : {
     883             :         const struct elektraNi_node_struct * n;
     884         227 :         n = (const struct elektraNi_node_struct *) item;
     885             : 
     886             :         assert (item_size == sizeof (struct elektraNi_node_struct));
     887             :         assert (key != NULL);
     888             :         assert (n->name != NULL);
     889             :         assert (key_size < elektraNi_KEY_SIZE);
     890             :         assert (n->name_len < elektraNi_KEY_SIZE);
     891         227 :         const size_t n_name_len = n->name_len;
     892             : 
     893         227 :         return (key_size != n_name_len || memcmp (key, n->name, key_size));
     894             : }

Generated by: LCOV version 1.13