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