Provided by: elektra-doc_0.8.14-5.1ubuntu2_all bug

NAME

       doc_DESIGN_mdDESIGN
        - This document describes the design of the c-api and gives hints for binding writers. It does not aim
       to plugin writers because this detail is hidden from the programmer and is Elektra specific.

       Elektra follows the design principles to make it hard to use it wrong and fully aims towards an easy to
       use API for programmers reading and writing configuration. The data structures are optimized to get, set
       and lookup values easily and fast.

       The idea is that the KDB API is not only implemented by Elektra. Elektra provides a full blown
       architecture to really support modern Linux Systems, but comes with some overhead. In this document the
       KDB API is described. But sometimes there are hints for elektra specific conventions.

   Data Structures
       Key, KeySet and KDB datastructures are defined in kdbprivate.h to remain ABI compatible when something is
       added to a struct. That means it is not possible to put one of elektra's datastructures on the stack. You
       must use the memory management facilities mentioned in the next chapter.

   Memory Management
       Elektra manages memory itself. No free must be required, which was not allocated by the programmer
       himself. This avoids that free is forgotten, makes the API more beginner-friendly. In addition to all
       that malloc and free must have the same libc version. malloc in a library linked against another libc,
       but freed by the application could lead to hard to find bugs.

       Some calls have a opposite call to get the structure freed again:

       KDB * kdbOpen();

        will need the function:

           int kdbClose(KDB *handle);

        to get rid of the resources again. It maybe also shut down connections, so it really must be called at
       the end of the program.

           Key *keyNew(const char *keyName, ...);
           int keyDel(Key *key);

           KeySet *ksNew(int alloc, ...);
           int ksDel(KeySet *ks);

       These 2 pairs just malloc what is necessary and free it again. There are more mallocs then just the KDB,
       Key and KeySet structures, but they are invisible and they happen implicit within any of these 3 classes.

       Name, Value, Comment can't be handled as easy, because elektra does not provide a string library. There
       are 2 ways to access it, showed on the Comment example:

           char *keyComment(const Key *key);

        just returns the comment and does not allow any change of size of the comment.

           ssize_t keyGetCommentSize(const Key *key);

        to see how long the comment is for above function and how much buffer to allocate for function below.
       This value can be directly passed to malloc.

           ssize_t keyGetComment(const Key *key, char *returnedDesc, size_t maxSize);

        will write the comment in a buffer maintained by you which is allocated with at least the size of the
       function above.

   Variable Arguments
       The constructors for Key and KeySet take a variable list of arguments as alternative to keySet* functions
       and to ksAppendKey(). With that you can generate any key or keyset in a single c-statement. This can be
       done programmatically by keyGenerate or ksGenerate in libelektratools.

       To just get a key, use

           Key *k = keyNew (0);

        and to just get a keyset, use

           KeySet *k = ksNew(0, KS_END);

       The macros va_start and va_end will not be used then. Otherwise pass a list like described in the
       documentation.

   Off-by-one
       Off-by-one errors (OBOE) are avoided by starting all pointers with 0 as usual in C. The size returned by
       the *GetSize functions (keyGetValueSize, keyGetCommentSize and keyGetOwnerSize) is exactly the size
       needed to be allocated. So if you add 1 to it, too much is allocated, but no error will occur.

   Minimal Set
       The functions listed in kdb.h is a minimal set to fully work with a key database. They are implemented in
       src/libelektra in ANSI C.

       Functions used by backends are implemented in src/backends/helpers. They need the POSIX interface and can
       optionally use iconv to make utf8 conversations.

   Value, String or Binary
       Some confusion is about value, string or binary. Value is just a name which does not specify if it is a
       string or binary. String is a char array, with a terminating '\0', but Binary is a void array, not
       terminated by '\0'. Only strings may be converted to other charsets. Use the appendant Get functions, to
       be not depend on that internal facts.

           const void *keyValue(const Key *key);

        does not specify whether it is a binary or string, it will just return the pointer to it. When Key is a
       string (check with keyIsString()) at least '' will be returned, see below Return Values for strings. For
       binary data a NULL pointer is also possible to distinguish between no data and '\0'.

           ssize_t keyGetValueSize(const Key *key);

        does not specify whether it is a binary or string, it will just return the size which can be passed to
       malloc to hold the entire value.

           ssize_t keyGetString(const Key *key, char *returnedString, size_t maxSize);

        Get the string into a buffer maintained by you.

           ssize_t keySetString(Key *key, const char *newString);

        Set the null terminated string.

           ssize_t keyGetBinary(const Key *key, void *returnedBinary, size_t maxSize);

        Get the binary data which might contain '\0'.

           ssize_t keySetBinary(Key *key, const void *newBinary, size_t dataSize);

        Set the binary data which might contain '\0'. The length is given by dataSize.

   Return Value
       There are many different types of return values. What they have in common is there error behaviour. Every
       function must return -1 on error if it returns Integers (like int, ssize_t). If they return a pointer, 0
       (NULL) will show the error. This can't be used for Integers, because 0 might be a valid size or is used
       as 'false'.

       Elektra does not use any float or double, so there is no issue there.

       But Elektra uses integers for c-string length, reference counting, keyset length and internal keyset
       allocations.

       The interface always accept size_t and uses internal size_t which can hold larger numbers then ssize_t.
       But to indicate an error it must be always possible to return -1. This is only possible with int or
       ssize_t. These two are used for tests and to return a size.

       So the real size of c-strings and buffers is limited to SSIZE_MAX which must be checked in every
       function. When it exceeds that limit -1 (see above Return Value) or NULL pointer must be returned.

       There are some functions which return an internal string:

           const char *keyName(const Key *key);
           const char *keyBaseName(const Key *key);
           const char *keyOwner(const Key *key);
           const char *keyComment(const Key *key);

       and in the case that (keyIsBinary(key)==1) also:

           const void *keyValue(const Key *key);

       A Null pointer will lead in all that cases that you get back a Null pointer.

       When there is no string you will get back '', that is a pointer to '\0'. It marks that there is no
       string. The function to determine the size will return 1 in that case. That means that 1 is for empty
       strings (Nothing except the NULL terminator).

       This is not true for keyValue in the case of binary data, because '\0' in the first byte is a perfect
       legal binary data. keyGetValueSize() may also return 0 for that reason.

   Error Handling
       Error handling will not be implemented with 0.7.0. So it won't be possible for an application to
       determine what exactly went wrong.

       Elektra does not set errno. If a function you call sets errno, make sure to set it back to the old value
       again.

   Naming
       All Function Names begin with their class name, e.g. kdb, ks or key. The words are written together with
       large letters for separation. This leads to short names, but might be not as good to read. Get and Set
       are used for getters/and setters, Is to ask about a flag or state, Needs to ask about state related to
       database and e.g. keyRemove to set a flag or state. For allocation/deallocation the names are c++
       stylish, like the *New functions *Del.

       Macros and Enums are written in capital letters. Options start with KDB_O, errors KDB_ERR, namespaces
       KEY_NS and key types with KEY_TYPE.

       The data structures start with a capital letter for every part of the word:

           KDB ... Key Data Base Handle
           KeySet ... Key Set
           Key ... Key

       keyGetUID() and keyGetGID() have upper case letters because ID is commonly written in upper case letters.

       Only singular is used for all names.

       Function Names not belonging to one of the three classes are Elektra specific. Those have the prefix
       elektra*. They will always be Elektra specific and won't be implemented by other KDB implementations.

   const
       Where possible the functions should use const for parameters. Where Key or KeySet is not modified, const
       is used. For return values no const is used, its more disturbing then have any positive effect. The only
       exceptions are:

       In special:

           const char *keyName(const Key *key);
           const char *keyBaseName(const Key *key);
           const char *keyComment(const Key *key);
           const void *keyValue(const Key *key);
           const char *keyString(const Key *key);
           const Key  *keyGetMeta(const Key *key, const char* metaName)

       These functions are really thought to get something and not to change anything! Elektra will lose the
       knowledge if these keys are synchronized or not. So they are marked const, and you must not cast that
       away.