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 : /**
12 : * @brief Iterate over string and open everything
13 : *
14 : * @pre Sometimes the first or last value needs special handling, the
15 : * caller needs to do that.
16 : * Implements lookahead for arrays, but does not implement leaf
17 : * semantics.
18 : *
19 : * @pre g must be in a map environment
20 : *
21 : * @post g is left in map or array environment
22 : *
23 : *
24 : * It handles following scenarios:
25 : *
26 : * (N0)
27 : * #/_
28 : * found array start, but followed by non-array so we need a map
29 : * yield array and map (name of array done already)
30 : *
31 : * (N1)
32 : * #
33 : * found array start, yield array (name done already)
34 : *
35 : * (N2)
36 : * _/# (or empty array/map)
37 : *
38 : * found array name, yield string (array done later)
39 : *
40 : * (N3)
41 : * _
42 : * found map start, yield string + map
43 : *
44 : * @param g to yield maps, strings
45 : * @param pnext a pointer to the name of the key at the correct pos
46 : * @param levels to iterate, if smaller or equal zero it does nothing
47 : */
48 236 : static void elektraGenOpenIterate (yajl_gen g, const char * pnext, int levels)
49 : {
50 236 : size_t size = 0;
51 :
52 : ELEKTRA_LOG_DEBUG ("levels: %d, next: \"%s\"", levels, pnext);
53 :
54 420 : for (int i = 0; i < levels; ++i)
55 : {
56 184 : pnext = keyNameGetOneLevel (pnext + size, &size);
57 :
58 184 : lookahead_t lookahead = elektraLookahead (pnext, size);
59 :
60 : ELEKTRA_LOG_DEBUG ("level by name %d: \"%.*s\", lookahead: %d", (int) i, (int) size, pnext, lookahead);
61 :
62 : // do not yield array indizes as names
63 184 : if (*pnext == '#')
64 : {
65 : ELEKTRA_LOG_DEBUG ("GEN (N1) array by name");
66 54 : yajl_gen_array_open (g);
67 :
68 54 : if (lookahead == LOOKAHEAD_MAP)
69 : {
70 : ELEKTRA_LOG_DEBUG ("GEN (N0) anon-map");
71 30 : yajl_gen_map_open (g);
72 : }
73 : }
74 130 : else if (lookahead == LOOKAHEAD_ARRAY || lookahead == LOOKAHEAD_EMPTY_ARRAY || lookahead == LOOKAHEAD_EMPTY_MAP)
75 : {
76 : ELEKTRA_LOG_DEBUG ("GEN (N2) string for start/empty array/map %.*s", (int) size, pnext);
77 48 : yajl_gen_string (g, (const unsigned char *) pnext, size);
78 :
79 : // opening (empty) array will be handled later
80 : }
81 : else
82 : {
83 : ELEKTRA_LOG_DEBUG ("GEN (N3) string %.*s", (int) size, pnext);
84 82 : yajl_gen_string (g, (const unsigned char *) pnext, size);
85 :
86 : ELEKTRA_LOG_DEBUG ("GEN (N3) map by name");
87 82 : yajl_gen_map_open (g);
88 : }
89 : }
90 236 : }
91 :
92 :
93 : /**
94 : * @brief fixes elektraGenOpenIterate for the special handling of
95 : * arrays at very last position.
96 : *
97 : * @param g generate array there
98 : * @param key the key to look at
99 : */
100 236 : static void elektraGenOpenLast (yajl_gen g, const Key * key)
101 : {
102 236 : keyNameReverseIterator last = elektraKeyNameGetReverseIterator (key);
103 236 : elektraKeyNameReverseNext (&last);
104 :
105 : ELEKTRA_LOG_DEBUG ("last startup entry: \"%.*s\"", (int) last.size, last.current);
106 :
107 236 : if (last.current[0] == '#' && strcmp (last.current, "###empty_array"))
108 : {
109 : // is an array, but not an empty one
110 : ELEKTRA_LOG_DEBUG ("GEN array open last");
111 57 : yajl_gen_array_open (g);
112 : }
113 236 : }
114 :
115 : /**
116 : * @brief Open initially
117 : *
118 : * Unlike in elektraGenOpen there is no precondition that parentKey
119 : * must be below first. Instead:
120 : *
121 : * @pre first must be below parentKey
122 : *
123 : * @pre g expects to be in a map
124 : *
125 : * @post g left in map or array
126 : *
127 : * Does not have special handling for first key (only for last key)
128 : *
129 : * @see elektraGenOpen
130 : *
131 : * @param g
132 : * @param parentKey
133 : * @param first
134 : */
135 66 : void elektraGenOpenInitial (yajl_gen g, Key * parentKey, const Key * first)
136 : {
137 66 : const char * pfirst = keyName (first);
138 66 : size_t csize = 0;
139 :
140 66 : int equalLevels = elektraKeyCountEqualLevel (parentKey, first);
141 66 : int firstLevels = elektraKeyCountLevel (first);
142 :
143 : // forward all equal levels
144 334 : for (int i = 0; i < equalLevels + 1; ++i)
145 : {
146 268 : pfirst = keyNameGetOneLevel (pfirst + csize, &csize);
147 : }
148 :
149 : // calculate levels: do not iterate over last element
150 66 : const int levelsToOpen = firstLevels - equalLevels - 1;
151 :
152 : ELEKTRA_LOG_DEBUG ("open by name Initial %s, equal: %d, to open: %d", pfirst, equalLevels, levelsToOpen);
153 :
154 :
155 66 : if (pfirst && *pfirst == '#')
156 : {
157 : ELEKTRA_LOG_DEBUG ("array open INITIAL");
158 : }
159 : else
160 : {
161 : ELEKTRA_LOG_DEBUG ("GEN map open INITIAL");
162 60 : yajl_gen_map_open (g);
163 : }
164 :
165 :
166 66 : elektraGenOpenIterate (g, pfirst, levelsToOpen);
167 :
168 66 : elektraGenOpenLast (g, first);
169 66 : }
170 :
171 :
172 : /**
173 : * @brief Implements special handling for the first found name
174 : *
175 : * @pre expects to be in a map or array
176 : *
177 : * @post will leave in a map or array
178 : *
179 : * Looks at first place where two key name differ and need a lookahead
180 : * of one in next to not generate too much.
181 : *
182 : * It handles the first level entirely, except of values.
183 : *
184 : * _ .. is an arbitrary map
185 : * # .. is an arbitrary member of array
186 : * Note that "test/" would not be contained in the string
187 : * given by argument.
188 : *
189 : * (O1)
190 : * cur: test/#
191 : * next: test/#
192 : *
193 : * Will do nothing, because we are in a situation where we just iterate
194 : * over leaves in the array.
195 : *
196 : * (O2)
197 : * cur: test/#
198 : * next: test/#/#
199 : *
200 : * Will create an array in the array.
201 : * Array won't have a name.
202 : * Staying in the same array "test".
203 : *
204 : * (O3)
205 : * cur: test/#
206 : * next: test/#/_
207 : *
208 : * Will yield a map (name will be yield later).
209 : * Staying in the same array.
210 : *
211 : * (O4)
212 : * cur: test/_
213 : * next: test/_
214 : *
215 : * Will yield a value. (Value yield later)
216 : *
217 : * (O5)
218 : * cur: test/_
219 : * next: test/_/# (or empty map/array)
220 : *
221 : * Will yield the name of the array (array handled later)
222 : *
223 : * (O6s O6m O6e)
224 : * cur: test/_
225 : * next: test/_/_
226 : *
227 : * Will yield the name (O6s) of the map and the map (O6m)
228 : * if it is an empty map, do not yield a map (O6e)
229 : *
230 : * @pre
231 : * (P1) Precondition
232 : * cur: test/_
233 : * next: test
234 : *
235 : * Is not possible.
236 : * @see elektraNextNotBelow
237 : *
238 : * @pre
239 : * (P2) Precondition
240 : * cur: test
241 : * next: test/_
242 : *
243 : * Is not possible.
244 : * @see elektraGenOpenInitial
245 : *
246 : * (E1)
247 : * cur: test/_
248 : * next: test/#
249 : *
250 : * Error situation: cannot change to array within map.
251 : * Lookahead does not matter.
252 : *
253 : * (E2)
254 : * cur: test/#
255 : * next: test/_
256 : *
257 : * Error situation: leaving array in the middle.
258 : * Lookahead does not matter.
259 : *
260 : * Rationale for arrays:
261 : * Error situations should be handled by struct checker.
262 : * They could be avoided by encoding array names like
263 : * array#0, but this would lead to the possibility to create
264 : * non-unique names which would be kicked away in json.
265 : * It is considered that always generating correct files
266 : * is more important than not having the possibility to generate
267 : * wrong keysets for a particual storage.
268 : *
269 : * @param g
270 : * @param cur
271 : * @param next
272 : */
273 170 : static void elektraGenOpenFirst (yajl_gen g, const char * cur, const char * next, size_t nextSize)
274 : {
275 170 : lookahead_t lookahead = elektraLookahead (next, nextSize);
276 : ELEKTRA_LOG_DEBUG ("cur: \"%s\" next: \"%s\", lookahead: %d", cur, next, lookahead);
277 :
278 170 : if (*cur == '#')
279 : {
280 68 : if (*next == '#')
281 : {
282 68 : if (lookahead == LOOKAHEAD_MAP)
283 : {
284 : ELEKTRA_LOG_DEBUG ("GEN (O3) next anon map");
285 26 : yajl_gen_map_open (g);
286 : }
287 : else
288 : {
289 : ELEKTRA_LOG_DEBUG ("we are iterating over array, nothing to do");
290 : }
291 : }
292 : else
293 : {
294 : ELEKTRA_LOG_DEBUG ("ERROR should not happen");
295 : }
296 : }
297 : else
298 : {
299 102 : if (lookahead == LOOKAHEAD_END)
300 : {
301 : ELEKTRA_LOG_DEBUG ("GEN string (O4)");
302 0 : yajl_gen_string (g, (const unsigned char *) next, nextSize);
303 : }
304 102 : else if (lookahead == LOOKAHEAD_ARRAY || lookahead == LOOKAHEAD_EMPTY_ARRAY || lookahead == LOOKAHEAD_EMPTY_MAP)
305 : {
306 : ELEKTRA_LOG_DEBUG ("GEN string for start/empty array (O5)");
307 45 : yajl_gen_string (g, (const unsigned char *) next, nextSize);
308 : // opening (empty) array will be handled later
309 : }
310 57 : else if (lookahead == LOOKAHEAD_MAP)
311 : {
312 : ELEKTRA_LOG_DEBUG ("GEN string (O6s)");
313 57 : yajl_gen_string (g, (const unsigned char *) next, nextSize);
314 :
315 : ELEKTRA_LOG_DEBUG ("GEN map (O6m)");
316 57 : yajl_gen_map_open (g);
317 : }
318 : }
319 170 : }
320 :
321 : /**
322 : * @brief open so many levels as needed for key next
323 : *
324 : * Iterates over next and generate
325 : * needed groups for every name/ and needed arrays
326 : * for every name/#.
327 : *
328 : * Yields names for leafs, but suppresses to yield names
329 : * for array entries (like #0)
330 : *
331 : * @see elektraGenOpenFirst
332 : *
333 : * @pre keys are not allowed to be below
334 : *
335 : * @example
336 : *
337 : * Example for elektraNextNotBelow:
338 : * cur: user/sw/org
339 : * next: user/sw/org/deeper
340 : * -> do nothing, "deeper" is value
341 : *
342 : * -- cut --
343 : *
344 : * cur: user/sw/org/deeper
345 : * next: user/sw/org/other
346 : * -> this cannot happen (see elektraNextNotBelow)
347 : *
348 : * cur: user/sw/org/other
349 : * next: user/sw/org/other/deeper/below
350 : * -> this cannot happen (see elektraNextNotBelow)
351 : *
352 : * -- cut --
353 : *
354 : * instead of cut two entries above following would happen:
355 : * cur: user/sw/org/deeper
356 : * next: user/sw/org/other/deeper/below
357 : * -> and "other" and "deeper" would be opened
358 : *
359 : * cur: user/sw/org/other/deeper/below
360 : * next: user/no
361 : * -> do nothing, because "no" is value
362 : *
363 : * cur: user/no
364 : * next: user/oops/it/is/below
365 : * -> create map "oops" "it" "is"
366 : *
367 : * cur: user/oops/it/is/below
368 : * next: user/x/t/s/x
369 : * -> create "x" "t" "s"
370 : *
371 : * @example
372 : *
373 : * cur: user/sw/org/#0
374 : * next: user/sw/org/#1
375 : * -> will not open org or array (because that did not change),
376 : * but will open group test (because within arrays every key
377 : * needs a group).
378 : *
379 : * cur: user/sw/org/#0
380 : * next: user/sw/oth/#0
381 : * -> will open new group oth and new array and yield blah
382 : *
383 : * cur: user/sw
384 : * next: user/sw/array/#0
385 : * -> will yield a new array using name "array"
386 : *
387 : * @pre cur and next have a name which is not equal
388 : *
389 : * @param g handle to generate to
390 : * @param cur current key of iteration
391 : * @param next next key of iteration
392 : */
393 777 : void elektraGenOpen (yajl_gen g, const Key * cur, const Key * next)
394 : {
395 777 : const char * pcur = keyName (cur);
396 777 : const char * pnext = keyName (next);
397 : // size_t curLevels = elektraKeyCountLevel(cur);
398 777 : size_t nextLevels = elektraKeyCountLevel (next);
399 777 : size_t size = 0;
400 777 : size_t csize = 0;
401 :
402 777 : size_t equalLevels = elektraKeyCountEqualLevel (cur, next);
403 :
404 : // forward all equal levels
405 6590 : for (size_t i = 0; i < equalLevels + 1; ++i)
406 : {
407 5813 : pnext = keyNameGetOneLevel (pnext + size, &size);
408 5813 : pcur = keyNameGetOneLevel (pcur + csize, &csize);
409 : }
410 :
411 : // always skip first and last level
412 777 : const int levelsToSkip = 2;
413 :
414 : // calculate levels which are neither already handled
415 : // nor the last one
416 777 : int levels = (int) nextLevels - (int) (equalLevels + levelsToSkip);
417 :
418 777 : int actionRequired = equalLevels + 1 < nextLevels;
419 :
420 : ELEKTRA_LOG_DEBUG ("%d: pcur: %s , pnext: %s, action: %d", (int) levels, pcur, pnext, actionRequired);
421 :
422 : // check if anything needs to be done at all
423 777 : if (actionRequired)
424 : {
425 170 : elektraGenOpenFirst (g, pcur, pnext, size);
426 :
427 : // skip the first level we did already
428 170 : pnext = keyNameGetOneLevel (pnext + size, &size);
429 :
430 : // now yield everything else in the string but the last value
431 170 : elektraGenOpenIterate (g, pnext, levels);
432 :
433 170 : elektraGenOpenLast (g, next);
434 : }
435 777 : }
|