Git init
[pkgs/e/elektra.git] / src / libelektra / keytest.c
1  /***************************************************************************
2                       keytest.c  -  Methods for Key manipulation
3                              -------------------
4     begin                : Fri Sep 26 2008
5     copyright            : (C) 2008 by Markus Raab
6     email                : elektra@markus-raab.org
7  ***************************************************************************/
8
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the BSD License (revised).                      *
13  *                                                                         *
14  ***************************************************************************/
15
16
17 /**
18  * @defgroup keytest Key :: Methods for Making Tests
19  * @brief Methods to do various tests on Keys
20  *
21  * To use them:
22  * @code
23 #include <kdb.h>
24  * @endcode
25  *
26  *
27  */
28
29
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #if DEBUG && HAVE_STDIO_H
36 #include <stdio.h>
37 #endif
38
39 #ifdef HAVE_STDARG_H
40 #include <stdarg.h>
41 #endif
42
43 #ifdef HAVE_STRING_H
44 #include <string.h>
45 #endif
46
47 #ifdef HAVE_STDLIB_H
48 #include <stdlib.h>
49 #endif
50
51 #include "kdb.h"
52 #include "kdbprivate.h"
53
54
55
56 /**
57  * Ask if key is marked for stat only.
58  *
59  * Ask if the key will be stat instead of get it from the key database
60  * completely doing kdbGetKey() or kdbGet(). This is useful
61  * if you are not interested in the value, comment or key type.
62  *
63  * @see keyStat(), kdbGet()
64  * @param key the key object to work with
65  * @return 1 if it is marked, 0 otherwise
66  * @return -1 on NULL pointer
67  * @ingroup keytest
68  **/
69 int keyNeedStat(const Key *key)
70 {
71         if (!key) return -1;
72
73         return (key->flags & KEY_FLAG_STAT) == KEY_FLAG_STAT;
74 }
75
76
77
78 /**
79  * Test if a key needs to be synced to backend storage.
80  *
81  * If any key modification took place the key will be flagged with
82  * KEY_FLAG_SYNC so that kdbSet() knows which keys were modified
83  * and which not.
84  *
85  * After keyNew() the flag will normally be set, but after kdbGet()
86  * and kdbSet() the flag will be removed. When you modify the key
87  * the flag will be set again.
88  *
89  * In your application you can make use of that flag to know
90  * if you changed something in a key after a kdbGet() or kdbSet().
91  *
92  * @note Note that also changes in the meta data will set that flag.
93  *
94  * @see keyNew()
95  * @param key the key object to work with
96  * @return 1 if @p key was changed in memory, 0 otherwise
97  * @return -1 on NULL pointer
98  * @ingroup keytest
99  */
100 int keyNeedSync(const Key *key)
101 {
102         if (!key) return -1;
103
104         return (key->flags & KEY_FLAG_SYNC) == KEY_FLAG_SYNC;
105 }
106
107
108 /**
109  * Ask if key is marked for permanent remove.
110  *
111  * Ask if the key will be removed instead of writing in the key database
112  * when doing kdbSetKey() or kdbSet().
113  *
114  * @see keyRemove()
115  * @see kdbSet(), kdbSetKey(), kdbRemove()
116  * @param key the key object to work with
117  * @return 1 if it is marked, 0 otherwise
118  * @return -1 on NULL pointer
119  * @ingroup keytest*/
120 int keyNeedRemove(const Key *key)
121 {
122         if (!key) return -1;
123
124         return (key->flags & KEY_FLAG_REMOVE) == KEY_FLAG_REMOVE;
125 }
126
127
128
129 /**
130  * Check whether a key is under the @p system namespace or not
131  *
132  * @param key the key object to work with
133  * @return 1 if key name begins with @p system, 0 otherwise
134  * @return -1 on NULL pointer
135  * @see keyIsUser(), keySetName(), keyName()
136  * @ingroup keytest
137  *
138  */
139 int keyIsSystem(const Key *key)
140 {
141         if (!key) return -1;
142
143         if (key->key) return keyNameIsSystem(key->key);
144         else return 0;
145 }
146
147
148
149 /**
150  * Check whether a key is under the @p memory namespace or not
151  *
152  * @param key the key object to work with
153  * @return 1 if key name begins with @p system, 0 otherwise
154  * @return -1 on NULL pointer
155  * @see keyIsUser(), keySetName(), keyName()
156  * @ingroup keytest
157  *
158  */
159 int keyIsMemory(const Key *key)
160 {
161         if (!key) return -1;
162
163         if (key->key) return keyNameIsMemory(key->key);
164    else return 0;
165 }
166
167
168
169 /**
170  * Check whether a key is under the @p user namespace or not.
171  *
172  * @param key the key object to work with
173  * @return 1 if key name begins with @p user, 0 otherwise
174  * @return -1 on NULL pointer
175  * @see keyIsSystem(), keySetName(), keyName()
176  * @ingroup keytest
177  *
178  */
179 int keyIsUser(const Key *key)
180 {
181         if (!key) return -1;
182
183         if (key->key) return keyNameIsUser(key->key);
184         else return 0;
185 }
186
187
188
189 /**
190  * Check if the key check is below the key key or not.
191  *
192  * @code
193 Example:
194 key user/sw/app
195 check user/sw/app/key
196
197 returns true because check is below key
198
199 Example:
200 key user/sw/app
201 check user/sw/app/folder/key
202
203 returns also true because check is indirect below key
204  * @endcode
205  *
206  * @param key the key object to work with
207  * @param check the key to find the relative position of
208  * @return 1 if check is below key
209  * @return 0 if it is not below or if it is the same key
210  * @see keySetName(), keyGetName(), keyIsDirectBelow()
211  * @ingroup keytest
212  *
213  */
214 int keyIsBelow(const Key *key, const Key *check)
215 {
216         const char * keyname = 0;
217         const char * checkname = 0;
218         ssize_t keysize = 0;
219         ssize_t checksize = 0;
220
221         if (!key || !check) return -1;
222
223         keyname = keyName(key);
224         checkname = keyName(check);
225         keysize = keyGetNameSize(key);
226         checksize = keyGetNameSize(check);
227
228         if (keysize > checksize + 1) return 0;
229         if (strncmp (keyname, checkname, keysize - 1)) return 0;
230         if (checkname[keysize - 1] != '/') return 0;
231         return 1;
232 }
233
234 /**
235  * Check if the key check is direct below the key key or not.
236  *
237  * @code
238 Example:
239 key user/sw/app
240 check user/sw/app/key
241
242 returns true because check is below key
243
244 Example:
245 key user/sw/app
246 check user/sw/app/folder/key
247
248 does not return true, because there is only a indirect relation
249  * @endcode
250  *
251  * @param key the key object to work with
252  * @param check the key to find the relative position of
253  * @return 1 if check is below key
254  * @return 0 if it is not below or if it is the same key
255  * @return -1 on null pointer
256  * @see keyIsBelow(), keySetName(), keyGetName()
257  * @ingroup keytest
258  *
259  */
260 int keyIsDirectBelow(const Key *key, const Key *check)
261 {
262         const char * checkname = 0;
263         ssize_t keysize = 0;
264
265         if (!key || !check) return -1;
266
267         checkname = keyName(check);
268         keysize = keyGetNameSize(key);
269
270         if (!keyIsBelow(key, check)) return 0;
271         if (strchr(checkname + keysize, '/')) return 0;
272         return 1;
273 }
274
275
276 /**
277  * Check whether a key is inactive or not.
278  *
279  * In elektra terminology any key is inactive if the
280  * it's basename starts with '.'. Inactive keys
281  * must not have any meaning to applications, they
282  * are reserved for users and administrators.
283  *
284  * To remove a whole hierarchy in elektra, don't
285  * forget to pass option_t::KDB_O_INACTIVE to
286  * kdbGet() to receive the inactive keys in order
287  * to remove them.
288  *
289  * Otherwise you should not fetch these keys.
290  *
291  * @param key the key object to work with
292  * @return 1 if the key is inactive, 0 otherwise
293  * @return -1 on NULL pointer or when key has no name
294  * @ingroup keytest
295  *
296  */
297 int keyIsInactive (const Key *key)
298 {
299         char *name = 0;
300
301         if (!key) return -1;
302
303         name = strrchr (keyName(key), '/');
304
305         if (!name) return -1;
306
307         /* the slash can't be a trailing slash
308         but there might be no name at all! */
309         if (name[1] == '.') return 1;
310         else return 0;
311 }
312
313
314
315
316 /**
317  * Check if a key is directory key.
318  *
319  * Folder keys may also have value and comment.
320  * They are discern by having a executable bit
321  * set.
322  *
323  * If any executable bit is set it will be recognized
324  * as a directory.
325  *
326  * @note keyIsDir may return true even though you
327  *   can't access the directory.
328  *
329  * To know if you can access the directory, you
330  * need to check, if your
331  * - user ID is equal the key's
332  *   user ID and the mode & 100 is true
333  * - group ID is equal the key's
334  *   group ID and the mode & 010 is true
335  * - mode & 001 is true
336  *
337  * Accessing does not mean that you can get any value or
338  * comments below, see @ref mode for more information.
339  *
340  * @param key the key object to work with
341  * @return 1 if key is a directory, 0 otherwise
342  * @return -1 on NULL pointer
343  * @see keySetDir(), keySetMode()
344  * @ingroup keytest
345  */
346 int keyIsDir(const Key *key)
347 {
348         if (!key) return -1;
349
350         return ((key->mode & KEY_DEF_DIR) != 0);
351 }
352
353
354 /**
355  * Check if a key is binary type.
356  *
357  * The function checks if the keytype is in the range between KEY_TYPE_BINARY and
358  * less than excluding KEY_TYPE_STRING. Then it will be interpreted as binary.
359  *
360  * Make sure to use this function and don't test the binary type another way to
361  * ensure compatibility and to write less error prone programs.
362  *
363  * @return 1 if it is binary
364  * @return 0 if it is not
365  * @return -1 on NULL pointer
366  * @see keySetType() for more information on types
367  * @see keyGetBinary(), keySetBinary()
368  * @param key the key to check
369  * @ingroup keytest
370  */
371 int keyIsBinary(const Key *key)
372 {
373         if (!key) return -1;
374
375         return (KEY_TYPE_BINARY <= key->type && key->type < KEY_TYPE_STRING);
376 }
377
378
379 /**
380  * Check if a key is string type.
381  *
382  * The function checks if the keytype is larger or equal KEY_TYPE_STRING.
383  * Then it will be considered as string type.
384  *
385  * Make sure to use this function and don't test the string type another way to
386  * ensure compatibility and to write less error prone programs.
387  *
388  * @return 1 if it is string
389  * @return 0 if it is not
390  * @return -1 on NULL pointer
391  * @see keySetType for more information on types
392  * @see keyGetString(), keySetString()
393  * @param key the key to check
394  * @ingroup keytest
395  */
396 int keyIsString(const Key *key)
397 {
398         if (!key) return -1;
399
400         return (key->type >= KEY_TYPE_STRING);
401 }
402
403
404
405
406
407 /*
408  * Compare 2 keys.
409  *
410  * The returned flags bit array has 1s (differ) or 0s (equal) for each key
411  * meta info compared, that can be logically ORed using @c #keyswitch_t flags.
412  * The flags you can use are @link keyswitch_t::KEY_TYPE KEY_TYPE
413  * @endlink, @link keyswitch_t::KEY_NAME KEY_NAME @endlink,
414  * @link keyswitch_t::KEY_VALUE KEY_VALUE @endlink,
415  * @link keyswitch_t::KEY_OWNER KEY_OWNER @endlink,
416  * @link keyswitch_t::KEY_COMMENT KEY_COMMENT @endlink,
417  * @link keyswitch_t::KEY_UID KEY_UID @endlink,
418  * @link keyswitch_t::KEY_GID KEY_GID @endlink,
419  * @link keyswitch_t::KEY_MODE KEY_MODE @endlink and
420  *
421  * @par A very simple example would be
422  * @code
423 Key *key1, *key;
424 uint32_t changes;
425
426 // omited key1 and key2 initialization and manipulation
427
428 changes=keyCompare(key1,key2);
429
430 if (changes == 0) printf("key1 and key2 are identicall\n");
431
432 if (changes & KEY_VALUE)
433         printf("key1 and key2 have different values\n");
434  
435 if (changes & KEY_UID)
436         printf("key1 and key2 have different UID\n");
437  
438  *
439  * @endcode
440  *
441  * 
442  * @par Example of very powerfull specific Key lookup in a KeySet:
443  * @code
444 KDB *handle = kdbOpen();
445 KeySet *ks=ksNew(0);
446 Key *base = keyNew ("user/sw/MyApp/something", KEY_END);
447 Key *current;
448 uint32_t match;
449 uint32_t interests;
450
451
452 kdbGetByName(handle, ks, "user/sw/MyApp", 0);
453
454 // we are interested only in key type and access permissions
455 interests=(KEY_TYPE | KEY_MODE);
456
457 ksRewind(ks);   // put cursor in the begining
458 while ((curren=ksNext(ks))) {
459         match=keyCompare(current,base);
460         
461         if ((~match & interests) == interests)
462                 printf("Key %s has same type and permissions of base key",keyName(current));
463
464         // continue walking in the KeySet....
465 }
466
467 // now we want same name and/or value
468 interests=(KEY_NAME | KEY_VALUE);
469
470 // we don't really need ksRewind(), since previous loop achieved end of KeySet
471 ksRewind(ks);
472 while ((current=ksNext(ks))) {
473         match=keyCompare(current,base);
474
475         if ((~match & interests) == interests) {
476                 printf("Key %s has same name, value, and sync status
477                         of base key",keyName(current));
478         }
479         // continue walking in the KeySet....
480 }
481
482 keyDel(base);
483 ksDel(ks);
484 kdbClose (handle);
485  * @endcode
486  * 
487  * @return a bit array pointing the differences
488  * @param key1 first key
489  * @param key2 second key
490  * @see #keyswitch_t
491  * @ingroup keytest
492  */
493 keyswitch_t keyCompare(const Key *key1, const Key *key2)
494 {
495         keyswitch_t ret=0;
496         const char *name1 = keyName(key1);
497         const char *name2 = keyName(key2);
498         const char *comment1 = keyComment(key1);
499         const char *comment2 = keyComment(key2);
500         const char *owner1 = keyOwner(key1);
501         const char *owner2 = keyOwner(key2);
502         const void *value1 = keyValue(key1);
503         const void *value2 = keyValue(key2);
504         int remove1 = keyNeedRemove(key1);
505         int remove2 = keyNeedRemove(key2);
506         ssize_t size1 = keyGetValueSize(key1);
507         ssize_t size2 = keyGetValueSize(key2);
508
509
510         if (key1->uid != key2->uid)        ret|=KEY_UID;
511         if (key1->gid != key2->gid)        ret|=KEY_GID;
512         if (key1->type != key2->type)      ret|=KEY_TYPE;
513         if ((key1->mode) != (key2->mode))  ret|=KEY_MODE;
514         if (remove1 != remove2)            ret|=KEY_REMOVE;
515         if (strcmp(name1, name2))          ret|=KEY_NAME;
516         if (strcmp(comment1, comment2))    ret|=KEY_COMMENT;
517         if (strcmp(owner1, owner2))        ret|=KEY_OWNER;
518         if (size1 != size2)                ret|=KEY_VALUE;
519         if (memcmp(value1, value2, size1)) ret|=KEY_VALUE;
520
521         return ret;
522 }
523
524