forked from ElektraInitiative/libelektra
-
Notifications
You must be signed in to change notification settings - Fork 0
/
keytest.c
340 lines (297 loc) · 7.51 KB
/
keytest.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
/**
* @file
*
* @brief Methods for making tests
*
* @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
*/
#ifdef HAVE_KDBCONFIG_H
#include "kdbconfig.h"
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include "kdb.h"
#include "kdbinternal.h"
#include "kdbprivate.h"
/**
* @defgroup keytest Methods for Making Tests
* @ingroup key
* @brief Methods to do various tests on Keys
*
* With exception of the parameters of keyCmp(), the following contract holds for all parameters of type Key:
* @pre The Key has been properly initialized via keyNew()
* @invariant All parts of the Key remain unchanged
* @post All parts of the Key are unchanged
*
* To use them:
* @code
#include <kdb.h>
* @endcode
*
*
*/
int keyIsSpec (const Key * key)
{
return keyGetNamespace (key) == KEY_NS_SPEC;
}
int keyIsProc (const Key * key)
{
return keyGetNamespace (key) == KEY_NS_PROC;
}
int keyIsDir (const Key * key)
{
return keyGetNamespace (key) == KEY_NS_DIR;
}
/**
* @internal
*
* Check whether a key is under the @p system namespace or not
*
* @param key the key object to work with
* @retval 1 if key name begins with @p system, 0 otherwise
* @see keyIsUser(), keySetName(), keyName()
* @ingroup keytest
*
*/
int keyIsSystem (const Key * key)
{
return keyGetNamespace (key) == KEY_NS_SYSTEM;
}
/**
* @internal
*
* Check whether a key is under the @p user namespace or not.
*
* @param key the key object to work with
* @retval 1 if key name begins with @p user, 0 otherwise
* @see keyIsSystem(), keySetName(), keyName()
* @ingroup keytest
*
*/
int keyIsUser (const Key * key)
{
return keyGetNamespace (key) == KEY_NS_USER;
}
/**
* Check if the Key @p check is below the Key @p key or not.
*
* Example:
@verbatim
key user:/sw/app
check user:/sw/app/key
@endverbatim
*
* returns true because @p check is below @p key
*
* Example:
@verbatim
key user:/sw/app
check user:/sw/app/folder/key
@endverbatim
*
* returns also true because @p check is indirectly below @p key
*
* Obviously, there is no Key above a namespace (e.g. user, system, /):
*
@verbatim
key *
check user
@endverbatim
*
* @param key the Key object to check against
* @param check the Key object for which it should be checked whether it is
* below @p key
*
* @retval 1 if @p check is below @p key
* @retval 0 if it is not below or if it is the same key
* @retval -1 if key or check is null
*
* @since 1.0.0
* @ingroup keytest
* @see keyIsDirectlyBelow() for checking whether a Key is directly below another
* @see keyGetName(), keySetName() for getting / setting the Key's name
*
*/
int keyIsBelow (const Key * key, const Key * check)
{
if (key == NULL || check == NULL)
{
return -1;
}
// same key, only if namespace and size are equal
// size alone could be equal with cascading keys
return keyIsBelowOrSame (key, check) && keyGetUnescapedNameSize (key) != keyGetUnescapedNameSize (check) &&
(keyGetNamespace (key) == keyGetNamespace (check) || keyGetNamespace (check) == KEY_NS_CASCADING ||
keyGetNamespace (key) == KEY_NS_CASCADING);
}
/**
* Check if a key is below or same.
*
* @param key the key object to work with
* @see keyIsBelow()
*/
int keyIsBelowOrSame (const Key * key, const Key * check)
{
if (key == NULL || check == NULL)
{
return -1;
}
const char * above = keyUnescapedName (key);
const char * below = keyUnescapedName (check);
size_t sizeAbove = keyGetUnescapedNameSize (key);
size_t sizeBelow = keyGetUnescapedNameSize (check);
if ((sizeAbove == 3 && above[0] == KEY_NS_CASCADING && sizeBelow == 3 && below[0] != KEY_NS_CASCADING) ||
(sizeBelow == 3 && below[0] == KEY_NS_CASCADING && sizeAbove == 3 && above[0] != KEY_NS_CASCADING))
{
// cascading root compared to other root
return 0;
}
if (sizeAbove == 3)
{
// root key, ignore trailing slash
sizeAbove -= 1;
}
if (sizeBelow == 3)
{
// root key, ignore trailing slash
sizeBelow -= 1;
}
if ((above[0] != KEY_NS_CASCADING && below[0] == KEY_NS_CASCADING) ||
(below[0] != KEY_NS_CASCADING && above[0] == KEY_NS_CASCADING))
{
// cascading, ignore namespaces
++above;
--sizeAbove;
++below;
--sizeBelow;
}
if (sizeAbove > sizeBelow)
{
return 0;
}
return memcmp (above, below, sizeAbove) == 0;
}
/**
* Check whether the Key @p check is directly below the Key @p key.
*
@verbatim
Example:
key user:/sw/app
check user:/sw/app/key
@endverbatim
*
* returns true because check is directly below key
*
@verbatim
Example:
key user:/sw/app
check user:/sw/app/folder/key
@endverbatim
*
* does not return true, because it is only indirectly below
*
* @param key the Key object to check against
* @param check the Key object for which it should be checked whether it is
* directly below @p key
*
* @retval 1 if @p check is directly below @p key
* @retval 0 if @p check is not directly below @p key or if it is the same
* @retval -1 on null pointer
*
* @since 1.0.0
* @ingroup keytest
* @see keyIsBelow() for checking whether a Key is below another
* @see keyGetName(), keySetName() for getting / setting the Key's name
*
*/
int keyIsDirectlyBelow (const Key * key, const Key * check)
{
if (key == NULL || check == NULL)
{
return -1;
}
const char * above = keyUnescapedName (key);
const char * below = keyUnescapedName (check);
size_t sizeAbove = keyGetUnescapedNameSize (key);
size_t sizeBelow = keyGetUnescapedNameSize (check);
if (sizeAbove == 3)
{
// root key, ignore trailing slash
sizeAbove -= 1;
}
if (sizeBelow == 3)
{
// root key, ignore trailing slash
sizeBelow -= 1;
}
if ((above[0] != KEY_NS_CASCADING && below[0] == KEY_NS_CASCADING) ||
(below[0] != KEY_NS_CASCADING && above[0] == KEY_NS_CASCADING))
{
// cascading, ignore namespaces
++above;
--sizeAbove;
++below;
--sizeBelow;
}
if (sizeAbove >= sizeBelow)
{
return 0;
}
size_t nextPartSize = strlen (below + sizeAbove);
return memcmp (above, below, sizeAbove) == 0 && sizeAbove + nextPartSize + 1 == sizeBelow;
}
/**
* Check if the value of a @p key is of binary type.
*
* The function checks if the value of @p key is binary. Contrary to string
* values binary values can have '\\0' inside the value and may not be
* terminated by a null character. Their disadvantage is that you need to pass
* their size.
*
* Make sure to use this function and don't test the binary type another way to
* ensure compatibility and to write less error prone programs.
*
* @param key the Key to check
*
* @retval 1 if the value of @p key is binary
* @retval 0 if the value of @p key is not binary
* @retval -1 on NULL pointer
*
* @ingroup keytest
* @see keyGetBinary(), keySetBinary() for getting / setting a Key's value as binary
*/
int keyIsBinary (const Key * key)
{
if (!key) return -1;
return keyGetMeta (key, "binary") != 0;
}
/**
* Check if the value of @p key is of string type.
*
* String values are null terminated and are not allowed to have any '\\0'
* characters inside the string.
*
* Make sure to use this function and don't test the string type another way to
* ensure compatibility and to write less error prone programs.
*
* @param key the Key to check
*
* @retval 1 if the value of @p key is string
* @retval 0 if the value of @p key is not string
* @retval -1 on NULL pointer
*
* @ingroup keytest
* @see keyGetString(), keySetString() for getting / setting a Key's value as string
*/
int keyIsString (const Key * key)
{
if (!key) return -1;
return keyGetMeta (key, "binary") == 0;
}