Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Implementation of proposed API enhancements.
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 :
10 : #include <ctype.h>
11 : #include <string.h>
12 :
13 : #include <kdbassert.h>
14 : #include <kdblogger.h>
15 : #include <kdbprivate.h>
16 :
17 : /**
18 : * @defgroup proposal Proposals for Elektra
19 : * @brief Might be added to, changed or removed from future Elektra releases.
20 : */
21 :
22 : /**
23 : * @defgroup api API Proposals for Elektra
24 : * @brief for kdb.h.
25 : * @ingroup proposal
26 : *
27 : * @warning Do not use these methods if you do not want to depend on
28 : * exactly the Elektra version your binary was built for.
29 : *
30 : * These methods are a technical preview of what might be added in
31 : * future Elektra releases. It is a requirement that methods are first
32 : * added here, before they are added to the public API.
33 : *
34 : * Usually, names in proposal stage should be prefixed with elektra to
35 : * clearly mark that the signature is likely to be changed and not yet
36 : * ABI compatible.
37 : *
38 : * @{
39 : */
40 :
41 :
42 : /**
43 : * @brief Set a formatted string
44 : *
45 : * @param key the key to set the string value
46 : * @param format NULL-terminated text format string
47 : * @param ... more arguments
48 : *
49 : * @return the size of the string as set (with including 0)
50 : */
51 26 : ssize_t keySetStringF (Key * key, const char * format, ...)
52 : {
53 : va_list arg_list;
54 :
55 26 : keySetMeta (key, "binary", 0);
56 :
57 26 : va_start (arg_list, format);
58 26 : char * p = elektraVFormat (format, arg_list);
59 26 : va_end (arg_list);
60 :
61 26 : if (!p)
62 : {
63 : return -1;
64 : }
65 :
66 26 : if (key->data.c && !test_bit (key->flags, KEY_FLAG_MMAP_DATA))
67 : {
68 22 : elektraFree (key->data.c);
69 : }
70 :
71 26 : key->data.c = p;
72 26 : key->dataSize = elektraStrLen (key->data.c);
73 26 : set_bit (key->flags, KEY_FLAG_SYNC);
74 :
75 26 : return key->dataSize;
76 : }
77 :
78 :
79 : /**
80 : * Builds an array of pointers to the keys in the supplied keyset.
81 : * The keys are not copied, calling keyDel may remove them from
82 : * the keyset.
83 : *
84 : * The size of the buffer can be easily allocated via ksGetSize. Example:
85 : * @code
86 : * KeySet *ks = somekeyset;
87 : * Key **keyArray = calloc (ksGetSize(ks), sizeof (Key *));
88 : * elektraKsToMemArray (ks, keyArray);
89 : * ... work with the array ...
90 : * elektraFree (keyArray);
91 : * @endcode
92 : *
93 : * @param ks the keyset object to work with
94 : * @param buffer the buffer to put the result into
95 : * @return the number of elements in the array if successful
96 : * @return a negative number on null pointers or if an error occurred
97 : */
98 3792 : int elektraKsToMemArray (KeySet * ks, Key ** buffer)
99 : {
100 3792 : if (!ks) return -1;
101 3790 : if (!buffer) return -1;
102 :
103 : /* clear the received buffer */
104 3788 : memset (buffer, 0, ksGetSize (ks) * sizeof (Key *));
105 :
106 3788 : cursor_t cursor = ksGetCursor (ks);
107 3788 : ksRewind (ks);
108 3788 : size_t idx = 0;
109 :
110 : Key * key;
111 32087 : while ((key = ksNext (ks)) != 0)
112 : {
113 24511 : buffer[idx] = key;
114 24511 : ++idx;
115 : }
116 3788 : ksSetCursor (ks, cursor);
117 :
118 3788 : return idx;
119 : }
120 :
121 : /**
122 : * @brief Takes the first key and cuts off this common part
123 : * for all other keys, instead name will be prepended
124 : *
125 : * @return a new allocated keyset with keys in user namespace.
126 : *
127 : * The first key is removed in the resulting keyset.
128 : */
129 0 : KeySet * ksRenameKeys (KeySet * config, const Key * name)
130 : {
131 0 : return elektraRenameKeys (config, keyName (name));
132 : }
133 :
134 : /**
135 : * @brief Permanently locks a part of the key
136 : *
137 : * This can be:
138 : * - KEY_FLAG_LOCK_NAME to lock the name
139 : * - KEY_FLAG_LOCK_VALUE to lock the value
140 : * - KEY_FLAG_LOCK_META to lock the metadata
141 : *
142 : * To unlock the key, duplicate it.
143 : *
144 : * It is also possible to lock when the key is created with
145 : * keyNew().
146 : *
147 : * Some data structures need to lock the key (most likely
148 : * its name), so that the ordering does not get confused.
149 : *
150 : * @param key which name should be locked
151 : *
152 : * @see keyNew(), keyDup(), ksAppendKey()
153 : * @retval >0 the bits that were successfully locked
154 : * @retval 0 if everything was locked before
155 : * @retval -1 if it could not be locked (nullpointer)
156 : */
157 6 : int keyLock (Key * key, option_t what)
158 : {
159 6 : return elektraKeyLock (key, what);
160 : }
161 :
162 :
163 : /**
164 : * @brief Return metadata as keyset
165 : *
166 : * @param key the key object to work with
167 : *
168 : * @return a duplication of the keyset representing the metadata
169 : */
170 58 : KeySet * elektraKeyGetMetaKeySet (const Key * key)
171 : {
172 58 : if (!key) return 0;
173 58 : if (!key->meta) return 0;
174 :
175 31 : return ksDup (key->meta);
176 : }
177 :
178 :
179 : /**
180 : * Returns the previous Key in a KeySet.
181 : *
182 : * KeySets have an internal cursor that can be reset with ksRewind(). Every
183 : * time ksPrev() is called the cursor is decremented and the new current Key
184 : * is returned.
185 : *
186 : * You'll get a NULL pointer if the key before begin of the KeySet was reached.
187 : *
188 : * Don't delete the key, use ksPop() if you want to delete it.
189 : *
190 : * @return the new current Key
191 : * @see ksRewind(), ksCurrent()
192 : *
193 : */
194 50 : Key * ksPrev (KeySet * ks)
195 : {
196 50 : return elektraKsPrev (ks);
197 : }
198 :
199 : /**
200 : * @brief Pop key at given cursor position
201 : *
202 : * @param ks the keyset to pop key from
203 : * @param c where to pop
204 : *
205 : * The internal cursor will be rewinded using ksRewind(). You can use
206 : * ksGetCursor() and ksSetCursor() jump back to the previous position.
207 : * e.g. to pop at current position within ksNext() loop:
208 : * @code
209 : * cursor_t c = ksGetCursor(ks);
210 : * keyDel (ksPopAtCursor(ks, c));
211 : * ksSetCursor(ks, c);
212 : * ksPrev(ks); // to have correct key after next ksNext()
213 : * @endcode
214 : *
215 : * @warning do not use, will be superseded by external iterator API
216 : *
217 : * @return the popped key
218 : * @retval 0 if ks is 0
219 : */
220 10 : Key * ksPopAtCursor (KeySet * ks, cursor_t pos)
221 : {
222 10 : return elektraKsPopAtCursor (ks, pos);
223 : }
224 :
225 :
226 : /**
227 : * keyRel replacement
228 : */
229 :
230 : // keyRel2 helper, turns key into a cascading key ( removes namespace)
231 172 : Key * keyAsCascading (const Key * key)
232 : {
233 172 : if (keyName (key)[0] == '/')
234 : {
235 26 : return keyDup (key);
236 : }
237 : else
238 : {
239 146 : elektraNamespace ns = keyGetNamespace (key);
240 146 : if (ns == KEY_NS_META || ns == KEY_NS_EMPTY || ns == KEY_NS_NONE)
241 : {
242 : // For metakeys or keys without namespace just prefix the keyname with a "/"
243 32 : Key * cKey = keyNew ("/", KEY_CASCADING_NAME, KEY_END);
244 32 : keyAddName (cKey, keyName (key));
245 32 : return cKey;
246 : }
247 : else
248 : {
249 : // Skip namespace
250 114 : const char * name = keyName (key);
251 114 : const char * ptr = strchr (name, '/');
252 114 : if (!ptr)
253 : {
254 4 : return keyNew ("/", KEY_CASCADING_NAME, KEY_END);
255 : }
256 : else
257 : {
258 110 : ssize_t length = keyGetNameSize (key);
259 110 : if ((ptr - name) == (length - 1))
260 : {
261 0 : return keyNew ("/", KEY_CASCADING_NAME, KEY_END);
262 : }
263 : else
264 : {
265 110 : return keyNew (ptr, KEY_CASCADING_NAME, KEY_END);
266 : }
267 : }
268 : }
269 : }
270 : }
271 :
272 : // keyRel2 helper, returns how many levels check is below key, or 0 if check isn't below
273 144 : int keyGetLevelsBelow (const Key * key, const Key * check)
274 : {
275 144 : const char * keyUName = keyUnescapedName (key);
276 144 : size_t keyUSize = keyGetUnescapedNameSize (key);
277 144 : const char * keyUNameEnd = keyUName + keyUSize;
278 :
279 144 : const char * checkUName = keyUnescapedName (check);
280 144 : size_t checkUSize = keyGetUnescapedNameSize (check);
281 144 : const char * checkUNameEnd = checkUName + checkUSize;
282 :
283 392 : while (strcmp (keyUName, checkUName) == 0)
284 : {
285 204 : keyUName = strchr (keyUName, '\0') + 1;
286 204 : checkUName = strchr (checkUName, '\0') + 1;
287 :
288 204 : if (keyUName >= keyUNameEnd)
289 : {
290 : int levels = 0;
291 246 : while (checkUName < checkUNameEnd)
292 : {
293 150 : checkUName = strchr (checkUName, '\0') + 1;
294 150 : ++levels;
295 : }
296 :
297 : return levels;
298 : }
299 :
300 108 : if (checkUName >= checkUNameEnd)
301 : {
302 : break;
303 : }
304 : }
305 :
306 : return 0;
307 : }
308 :
309 : /**
310 : * @brief Replacement proposal for keyRel
311 : * @return depending on relation type
312 : * @retval -1 usage error
313 : * @retval 0 test failed
314 : * @retval >1 true for binary tests, number of levels below for other relation tests
315 : *
316 : * @param key the key object to work with
317 : * @param check the second key object to check the relation with
318 : * @param which what kind of relationship test should be done
319 : */
320 :
321 :
322 82 : int keyRel2 (const Key * key, const Key * check, KeyRelType which)
323 : {
324 82 : if (!key || !check) return -1;
325 82 : if (!key->key || !check->key) return -1;
326 :
327 82 : Key * cKey = keyAsCascading (key);
328 82 : Key * cCheck = keyAsCascading (check);
329 82 : Key * cKeyParent = keyDup (cKey);
330 82 : keySetBaseName (cKeyParent, 0);
331 82 : if (keyName (cKeyParent)[0] == '\0') keySetName (cKeyParent, "/");
332 82 : int isBelow = 0;
333 82 : int isSilblingNephew = 0;
334 82 : isBelow = keyGetLevelsBelow (cKey, cCheck);
335 82 : if (!isBelow) isSilblingNephew = keyGetLevelsBelow (cKeyParent, cCheck);
336 82 : elektraNamespace keyNamespace = keyGetNamespace (key);
337 82 : elektraNamespace checkNamespace = keyGetNamespace (check);
338 82 : int retVal = 0;
339 82 : int bits = 0;
340 2706 : for (KeyRelType type = 1; type != 0; type <<= 1)
341 : {
342 2624 : if (type & which) ++bits;
343 : }
344 82 : if (bits != 1) return -1;
345 82 : switch (which)
346 : {
347 : case ELEKTRA_REL_BELOW_SAME_NS:
348 12 : if (isBelow && (keyNamespace == checkNamespace)) retVal = isBelow;
349 : break;
350 : case ELEKTRA_REL_BELOW_IGNORE_NS:
351 12 : if (isBelow) retVal = isBelow;
352 : break;
353 : case ELEKTRA_REL_BELOW_CASCADING_NS:
354 10 : if (isBelow && ((checkNamespace == KEY_NS_CASCADING) || (keyNamespace == KEY_NS_CASCADING))) retVal = isBelow;
355 : break;
356 : case ELEKTRA_REL_DIRECT_BELOW_SAME_NS:
357 6 : if ((isBelow == 1) && (keyNamespace == checkNamespace)) retVal = 1;
358 : break;
359 : case ELEKTRA_REL_DIRECT_BELOW_IGNORE_NS:
360 2 : if (isBelow == 1) retVal = 1;
361 : break;
362 : case ELEKTRA_REL_DIRECT_BELOW_CASCADING_NS:
363 2 : if ((isBelow == 1) && ((checkNamespace == KEY_NS_CASCADING) || (keyNamespace == KEY_NS_CASCADING))) retVal = 1;
364 : break;
365 : case ELEKTRA_REL_SILBLING_SAME_NS:
366 8 : if ((isSilblingNephew == 1) && (keyNamespace == checkNamespace)) retVal = 1;
367 : break;
368 : case ELEKTRA_REL_SILBLING_IGNORE_NS:
369 4 : if (isSilblingNephew == 1) retVal = 1;
370 : break;
371 : case ELEKTRA_REL_SILBLING_CASCADING_NS:
372 4 : if ((isSilblingNephew == 1) && ((checkNamespace == KEY_NS_CASCADING) || (keyNamespace == KEY_NS_CASCADING))) retVal = 1;
373 : break;
374 : case ELEKTRA_REL_NEPHEW_SAME_NS:
375 14 : if ((isSilblingNephew > 1) && (keyNamespace == checkNamespace)) retVal = isSilblingNephew - 1;
376 : break;
377 : case ELEKTRA_REL_NEPHEW_IGNORE_NS:
378 4 : if (isSilblingNephew > 1) retVal = isSilblingNephew - 1;
379 : break;
380 : case ELEKTRA_REL_NEPHEW_CASCADING_NS:
381 4 : if ((isSilblingNephew > 1) && ((checkNamespace == KEY_NS_CASCADING) || (keyNamespace == KEY_NS_CASCADING)))
382 4 : retVal = isSilblingNephew - 1;
383 : break;
384 : default:
385 : retVal = -1;
386 : break;
387 : }
388 82 : keyDel (cKey);
389 82 : keyDel (cCheck);
390 82 : keyDel (cKeyParent);
391 82 : return retVal;
392 : }
393 :
394 : /**
395 : * @}
396 : */
|