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