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 : }
|