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 fixes elektraGenCloseIterate for the special handling of
13 : * arrays at very last position.
14 : *
15 : * @param g generate array there
16 : * @param key the key to look at
17 : */
18 124 : static void elektraGenCloseLast (yajl_gen g, const Key * key)
19 : {
20 124 : keyNameReverseIterator last = elektraKeyNameGetReverseIterator (key);
21 124 : elektraKeyNameReverseNext (&last);
22 :
23 : ELEKTRA_LOG_DEBUG ("last startup entry: \"%.*s\"", (int) last.size, last.current);
24 :
25 124 : if (last.current[0] == '#' && strcmp (last.current, "###empty_array"))
26 : {
27 : ELEKTRA_LOG_DEBUG ("GEN array close last");
28 27 : yajl_gen_array_close (g);
29 : }
30 124 : }
31 :
32 :
33 : /**
34 : * @brief Close given number of levels of key
35 : *
36 : * @pre there is some needed special handling at begin at end,
37 : * the caller needs to do that
38 : *
39 : * For the basename of cur nothing needs to be done
40 : * (it was either a value or an array entry)
41 : *
42 : * Then for every level do:
43 : *
44 : *
45 : * (C1)
46 : * #/_
47 : * (lookahead says it is a map in the array)
48 : * -> close the anonymous map and then the array
49 : *
50 : * (C2)
51 : * _/#
52 : * _/___empty_map
53 : * (lookahead says it is not a map)
54 : * -> don't do anything
55 : *
56 : * (C3)
57 : * #
58 : * -> close the array
59 : *
60 : * (C4)
61 : * _
62 : * -> close the map
63 : *
64 : *
65 : * @param g to yield json information
66 : * @param cur the key which name is used for closing
67 : * @param levels the number of levels to close
68 : */
69 843 : static void elektraGenCloseIterate (yajl_gen g, const Key * cur, int levels)
70 : {
71 843 : keyNameReverseIterator curIt = elektraKeyNameGetReverseIterator (cur);
72 :
73 : // jump last element
74 843 : elektraKeyNameReverseNext (&curIt);
75 :
76 1017 : for (int i = 0; i < levels; ++i)
77 : {
78 174 : elektraKeyNameReverseNext (&curIt);
79 :
80 174 : lookahead_t lookahead = elektraLookahead (curIt.current, curIt.size);
81 :
82 174 : if (curIt.current[0] == '#')
83 : {
84 58 : if (lookahead == LOOKAHEAD_MAP)
85 : {
86 : ELEKTRA_LOG_DEBUG ("GEN (C1) anon map close");
87 30 : yajl_gen_map_close (g);
88 : }
89 : ELEKTRA_LOG_DEBUG ("GEN (C3) array close");
90 58 : yajl_gen_array_close (g);
91 : }
92 : else
93 : {
94 116 : if (lookahead == LOOKAHEAD_MAP)
95 : {
96 : ELEKTRA_LOG_DEBUG ("GEN (C4) map close");
97 73 : yajl_gen_map_close (g);
98 : }
99 : else
100 : {
101 : ELEKTRA_LOG_DEBUG ("(C2) lookahead not a map: nothing to do");
102 : }
103 : }
104 : }
105 843 : }
106 :
107 : /**
108 : * @brief Special handling of cases related to closing in non-final
109 : * situation.
110 : *
111 : * Closes the name in the middle (first unequal), so it must be executed
112 : * after iterating. (Because closing is in reverse order)
113 : *
114 : * Following situations are possible:
115 : *
116 : * (X1)
117 : * cur: #/#
118 : * next: #
119 : * -> closing array (only if levels <0, because it might be outside
120 : * array with iterating one level deeper)
121 : *
122 : * (X2)
123 : * cur: #/_
124 : * next: #
125 : * -> array iteration, but with anon map
126 : *
127 : * (X3)
128 : * cur: #
129 : * next: #
130 : * -> array iteration, so do not close array
131 : *
132 : * (X4)
133 : * cur: _/#
134 : * next: _
135 : * -> closing map, but only if levels <= 0 (means iteration did nothing)
136 : * (otherwise iteration already closed it)
137 : *
138 : * (X5)
139 : * cur: _/_
140 : * next: _
141 : * -> closing map (only if levels <= 0, because iteration did not do it)
142 : *
143 : * (X6)
144 : * cur: _
145 : * next: _
146 : * -> map iteration on same level, doing nothing
147 : *
148 : * @param g to generate to
149 : * @param pcur pointer to current name
150 : * @param csize size where cur has next level
151 : * @param pnext pointer to next name
152 : * @param levels how many levels were handled before (see examples
153 : * above)
154 : */
155 777 : static void elektraGenCloseFirst (yajl_gen g, const char * pcur, size_t csize, const char * pnext, int levels)
156 : {
157 777 : lookahead_t lookahead = elektraLookahead (pcur, csize);
158 : ELEKTRA_LOG_DEBUG ("%s -> %s, levels: %d, lookahead: %d", pcur, pnext, levels, lookahead);
159 777 : if (*pcur == '#' && *pnext == '#')
160 : {
161 330 : if (levels <= 0 && lookahead == LOOKAHEAD_ARRAY)
162 : {
163 : ELEKTRA_LOG_DEBUG ("GEN (X1) closing array in array");
164 10 : yajl_gen_array_close (g);
165 : }
166 320 : else if (lookahead == LOOKAHEAD_MAP)
167 : {
168 : ELEKTRA_LOG_DEBUG ("GEN (X2) next anon-map");
169 26 : yajl_gen_map_close (g);
170 : }
171 : else
172 : {
173 : ELEKTRA_LOG_DEBUG ("(X3) array iteration");
174 : }
175 : }
176 447 : else if (*pcur != '#')
177 : {
178 447 : if (levels <= 0 && lookahead == LOOKAHEAD_ARRAY)
179 : {
180 : ELEKTRA_LOG_DEBUG ("GEN (X4) closing array");
181 16 : yajl_gen_array_close (g);
182 : }
183 431 : else if (lookahead == LOOKAHEAD_MAP)
184 : {
185 : ELEKTRA_LOG_DEBUG ("GEN (X5) closing map");
186 66 : yajl_gen_map_close (g);
187 : }
188 : else
189 : {
190 : ELEKTRA_LOG_DEBUG ("(X6) same level iteration");
191 : }
192 : }
193 777 : }
194 :
195 : /**
196 : * @brief Close all levels in cur not needed in next
197 : *
198 : * Closing is much simpler then opening because no names need to be
199 : * yield.
200 : *
201 : * @pre keys are not allowed to be below,
202 : * except: last run where everything below root/parent key is
203 : * closed
204 : *
205 : * Then all levels are reverse iterated until the level before the equal
206 : * level.
207 : * @see elektraGenCloseIterate
208 : *
209 : * In the level before the equal level there is some special handling in
210 : * regards to the next level.
211 : * @see elektraGenCloseFirst
212 : *
213 : * @example
214 : *
215 : * cur: user/sw/org/deeper
216 : * next: user/sw/org/other/deeper/below
217 : * -> nothing will be done ("deeper" is value)
218 : * [eq: 3, cur: 4, next: 6, gen: 0]
219 : *
220 : * cur: user/sw/org/other/deeper/below
221 : * next: user/no
222 : * -> "deeper", "other", "org" and "sw" maps will be closed ("below" is value)
223 : * [eq: 1, cur: 6, next: 2, gen: 4]
224 : *
225 : * cur: user/no
226 : * next: user/oops/it/is/below
227 : * -> nothing will be done ("no" is value)
228 : * [eq: 1, cur: 2, next: 5, gen: 0]
229 : *
230 : * cur: user/oops/it/is/below
231 : * next: user/x/t/s/x
232 : * -> close "is", "it", "oops"
233 : * [eq: 1, cur: 5, next: 5, gen: 3]
234 : *
235 : * last iteration (e.g. close down to root)
236 : * cur: user/x/t/s/x
237 : * next: user
238 : * -> close "s", "t" and "x" maps
239 : * [eq: 1, cur: 5, next: 1, gen: 3]
240 : *
241 : * cur: user/#0/1/1/1
242 : * next: user/#1/1/1/1
243 : * -> close "1", "1", "1", but not array
244 : * [eq: 1, cur: 5, next: 5, gen: 3]
245 : *
246 : * @param g
247 : * @param cur
248 : * @param next
249 : */
250 777 : void elektraGenClose (yajl_gen g, const Key * cur, const Key * next)
251 : {
252 777 : int curLevels = elektraKeyCountLevel (cur);
253 : #ifdef HAVE_LOGGER
254 : int nextLevels = elektraKeyCountLevel (next);
255 : #endif
256 777 : int equalLevels = elektraKeyCountEqualLevel (cur, next);
257 :
258 : // 1 for last level not to iterate, 1 before 1 after equal
259 777 : int levels = curLevels - equalLevels - 2;
260 :
261 777 : const char * pcur = keyName (cur);
262 777 : size_t csize = 0;
263 777 : const char * pnext = keyName (next);
264 777 : size_t nsize = 0;
265 6590 : for (int i = 0; i < equalLevels + 1; ++i)
266 : {
267 5813 : pcur = keyNameGetOneLevel (pcur + csize, &csize);
268 5813 : pnext = keyNameGetOneLevel (pnext + nsize, &nsize);
269 : }
270 :
271 :
272 : ELEKTRA_LOG_DEBUG ("eq: %d, cur: %s %d, next: %s %d, levels: %d", equalLevels, pcur, curLevels, pnext, nextLevels, levels);
273 :
274 777 : if (levels > 0)
275 : {
276 58 : elektraGenCloseLast (g, cur);
277 : }
278 777 : elektraGenCloseIterate (g, cur, levels);
279 777 : elektraGenCloseFirst (g, pcur, csize, pnext, levels);
280 777 : }
281 :
282 : /**
283 : * @brief Close the last element
284 : *
285 : * Needs less special handling because cur is fully below next.
286 : *
287 : * Will fully iterate over all elements.
288 : *
289 : * @param g handle to yield close events
290 : * @param cur current key
291 : * @param next the last key (the parentKey)
292 : */
293 66 : void elektraGenCloseFinally (yajl_gen g, const Key * cur, const Key * next)
294 : {
295 66 : int curLevels = elektraKeyCountLevel (cur);
296 : #ifdef HAVE_LOGGER
297 : int nextLevels = elektraKeyCountLevel (next);
298 : #endif
299 66 : int equalLevels = elektraKeyCountEqualLevel (cur, next);
300 :
301 : // 1 for last level not to iterate, 1 after equal
302 66 : int levels = curLevels - equalLevels - 1;
303 :
304 66 : const char * pcur = keyName (cur);
305 66 : size_t csize = 0;
306 66 : const char * pnext = keyName (next);
307 66 : size_t nsize = 0;
308 334 : for (int i = 0; i < equalLevels + 1; ++i)
309 : {
310 268 : pcur = keyNameGetOneLevel (pcur + csize, &csize);
311 268 : pnext = keyNameGetOneLevel (pnext + nsize, &nsize);
312 : }
313 :
314 : ELEKTRA_LOG_DEBUG ("eq: %d, cur: %s %d, next: %s %d, levels: %d", equalLevels, pcur, curLevels, pnext, nextLevels, levels);
315 : // fixes elektraGenCloseIterate for the special handling of
316 : // arrays finally
317 66 : elektraGenCloseLast (g, cur);
318 :
319 : // now we iterate over the middle part
320 66 : elektraGenCloseIterate (g, cur, levels);
321 :
322 : // now we look at the first unequal element
323 : // this is the very last element we are about to close
324 66 : if (pcur && *pcur == '#')
325 : {
326 : ELEKTRA_LOG_DEBUG ("array close FINAL");
327 : }
328 : else
329 : {
330 : ELEKTRA_LOG_DEBUG ("GEN map close FINAL");
331 60 : yajl_gen_map_close (g);
332 : }
333 66 : }
|