Provided by: elektra-doc_0.8.14-5_all bug

NAME

       doc_DESIGN_md - DESIGN 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.