LCOV - code coverage report
Current view: top level - src/libs/meta - meta.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 350 376 93.1 %
Date: 2019-09-12 12:28:41 Functions: 26 26 100.0 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Methods for metadata manipulation.
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  */
       8             : 
       9             : #include <kdb.h>
      10             : #include <kdbconfig.h>
      11             : #include <kdbease.h>
      12             : #include <kdbmeta.h>
      13             : #include <kdbprivate.h>
      14             : #include <kdbproposal.h>
      15             : #include <kdbtypes.h>
      16             : #ifdef HAVE_STDIO_H
      17             : #include <stdio.h>
      18             : #endif
      19             : 
      20             : #ifdef HAVE_STDARG_H
      21             : #include <stdarg.h>
      22             : #endif
      23             : 
      24             : #ifdef HAVE_STRING_H
      25             : #include <string.h>
      26             : #endif
      27             : 
      28             : #ifdef HAVE_STDLIB_H
      29             : #include <stdlib.h>
      30             : #endif
      31             : 
      32             : #ifdef HAVE_ERRNO_H
      33             : #include <errno.h>
      34             : #endif
      35             : 
      36             : 
      37             : /**
      38             :  * @defgroup meta Meta Data proposal+compatibility
      39             :  * @brief Meta data proposal+compatibility methods.
      40             :  * @ingroup proposal
      41             :  *
      42             :  * In versions before Elektra 0.8 only limited metadata was
      43             :  * available. Now any metadata can be added. These API methods are
      44             :  * implementations of the 0.7 API using 0.8 metadata.
      45             :  *
      46             :  * Additionally, new suggestions can be made here.
      47             :  *
      48             :  * It is planned that these methods will be generated from doc/METADATA.ini
      49             :  * and moved to a separate library.
      50             :  * Currently, you should better avoid the methods and directly use  @link keymeta metainfo @endlink
      51             :  * instead.
      52             :  *
      53             :  * @{
      54             :  *
      55             :  */
      56             : 
      57             : 
      58             : /*********************************************
      59             :  *    General comment manipulation methods   *
      60             :  *********************************************/
      61             : 
      62             : 
      63             : /**
      64             :  * Return a pointer to the real internal @p key comment.
      65             :  *
      66             :  * This is a much more efficient version of keyGetComment() and you
      67             :  * should use it if you are responsible enough to not mess up things.
      68             :  * You are not allowed to change anything in the memory region the
      69             :  * returned pointer points to.
      70             :  *
      71             :  * keyComment() returns "" when there is no keyComment. The reason is
      72             :  * @code
      73             :  key=keyNew(0);
      74             :  keySetComment(key,"");
      75             :  keyComment(key); // you would expect "" here
      76             :  keyDel(key);
      77             :  * @endcode
      78             :  *
      79             :  * See keySetComment() for more information on comments.
      80             :  *
      81             :  * @note Note that the Key structure keeps its own size field that is calculated
      82             :  * by library internal calls, so to avoid inconsistencies, you
      83             :  * must never use the pointer returned by keyComment() method to set a new
      84             :  * value. Use keySetComment() instead.
      85             :  *
      86             :  * @param key the key object to work with
      87             :  * @return a pointer to the internal managed comment
      88             :  * @retval "" when there is no comment
      89             :  * @retval 0 on NULL pointer
      90             :  * @see keyGetCommentSize() for size and keyGetComment() as alternative
      91             :  */
      92        1070 : const char * keyComment (const Key * key)
      93             : {
      94             :         const char * comment;
      95             : 
      96        1070 :         if (!key) return 0;
      97        1066 :         comment = keyValue (keyGetMeta (key, "comment"));
      98             : 
      99        1066 :         if (!comment)
     100             :         {
     101             :                 /*errno=KDB_ERR_NOKEY;*/
     102             :                 return "";
     103             :         }
     104             : 
     105        1052 :         return comment;
     106             : }
     107             : 
     108             : 
     109             : /**
     110             :  * Calculates number of bytes needed to store a key comment, including
     111             :  * final NULL.
     112             :  *
     113             :  * Use this method to know to size for allocated memory to retrieve
     114             :  * a key comment.
     115             :  *
     116             :  * See keySetComment() for more information on comments.
     117             :  *
     118             :  * For an empty key name you need one byte to store the ending NULL.
     119             :  * For that reason 1 is returned.
     120             :  *
     121             :  * @code
     122             :  char *buffer;
     123             :  buffer = elektraMalloc (keyGetCommentSize (key));
     124             : // use this buffer to store the comment
     125             : // pass keyGetCommentSize (key) for maxSize
     126             :  * @endcode
     127             :  *
     128             :  * @param key the key object to work with
     129             :  * @return number of bytes needed
     130             :  * @retval 1 if there is no comment
     131             :  * @retval -1 on NULL pointer
     132             :  * @see keyGetComment(), keySetComment()
     133             :  */
     134        1302 : ssize_t keyGetCommentSize (const Key * key)
     135             : {
     136             :         ssize_t size;
     137        1302 :         if (!key) return -1;
     138             : 
     139        1298 :         size = keyGetValueSize (keyGetMeta (key, "comment"));
     140             : 
     141        1298 :         if (!size || size == -1)
     142             :         {
     143             :                 /*errno=KDB_ERR_NODESC;*/
     144             :                 return 1;
     145             :         }
     146             : 
     147        1196 :         return size;
     148             : }
     149             : 
     150             : 
     151             : /**
     152             :  * Get the key comment.
     153             :  *
     154             :  * @section comment Comments
     155             :  *
     156             :  * A Key comment is description for humans what this key is for. It may be a
     157             :  * textual explanation of valid values, when and why a user or administrator
     158             :  * changed the key or any other text that helps the user or administrator related
     159             :  * to that key.
     160             :  *
     161             :  * Don't depend on a comment in your program. A user is
     162             :  * always allowed to remove or change it in any way he wants to. But you are
     163             :  * allowed or even encouraged to always show the content of the comment
     164             :  * to the user and allow him to change it.
     165             :  *
     166             :  * @param key the key object to work with
     167             :  * @param returnedComment pre-allocated memory to copy the comments to
     168             :  * @param maxSize number of bytes that will fit returnedComment
     169             :  * @return the number of bytes actually copied to @p returnedString, including
     170             :  *      final NULL
     171             :  * @retval 1 if the string is empty
     172             :  * @retval -1 on NULL pointer
     173             :  * @retval -1 if maxSize is 0, not enough to store the comment or when larger then SSIZE_MAX
     174             :  * @see keyGetCommentSize(), keySetComment()
     175             :  */
     176         151 : ssize_t keyGetComment (const Key * key, char * returnedComment, size_t maxSize)
     177             : {
     178             :         const char * comment;
     179             :         size_t commentSize;
     180         151 :         if (!key) return -1;
     181             : 
     182         147 :         if (!maxSize) return -1;
     183         133 :         if (!returnedComment) return -1;
     184         129 :         if (maxSize > SSIZE_MAX) return -1;
     185             : 
     186         125 :         comment = keyValue (keyGetMeta (key, "comment"));
     187         125 :         commentSize = keyGetValueSize (keyGetMeta (key, "comment"));
     188             : 
     189         125 :         if (!comment)
     190             :         {
     191             :                 /*errno=KDB_ERR_NODESC;*/
     192          16 :                 returnedComment[0] = 0;
     193          16 :                 return 1;
     194             :         }
     195             : 
     196         109 :         strncpy (returnedComment, comment, maxSize);
     197         109 :         if (maxSize < commentSize)
     198             :         {
     199             :                 /*errno=KDB_ERR_TRUNC;*/
     200             :                 return -1;
     201             :         }
     202          63 :         return commentSize;
     203             : }
     204             : 
     205             : 
     206             : /**
     207             :  * Set a comment for a key.
     208             :  *
     209             :  * A key comment is like a configuration file comment.
     210             :  * See keySetComment() for more information.
     211             :  *
     212             :  * @param key the key object to work with
     213             :  * @param newComment the comment, that can be freed after this call.
     214             :  * @return the number of bytes actually saved including final NULL
     215             :  * @retval 0 when the comment was freed (newComment NULL or empty string)
     216             :  * @retval -1 on NULL pointer or memory problems
     217             :  * @see keyGetComment()
     218             :  */
     219        1199 : ssize_t keySetComment (Key * key, const char * newComment)
     220             : {
     221        1199 :         if (!key) return -1;
     222        1195 :         if (!newComment || *newComment == 0)
     223             :         {
     224          22 :                 keySetMeta (key, "comment", 0);
     225          22 :                 return 1;
     226             :         }
     227             : 
     228        1173 :         keySetMeta (key, "comment", newComment);
     229        1173 :         return keyGetCommentSize (key);
     230             : }
     231             : 
     232             : 
     233             : #ifndef _WIN32
     234             : 
     235             : /*********************************************
     236             :  *       UID, GID access methods             *
     237             :  *********************************************/
     238             : 
     239             : 
     240             : /**
     241             :  * Get the user ID of a key.
     242             :  *
     243             :  * @deprecated This API is obsolete.
     244             :  *
     245             :  * @section UID UID
     246             :  *
     247             :  * The user ID is a unique identification for every user present on a
     248             :  * system. Keys will belong to root (0) as long as you did not get their
     249             :  * real UID with kdbGet().
     250             :  *
     251             :  * Although usually the same, the UID of a key is not related to its owner.
     252             :  *
     253             :  * A fresh key will have no UID.
     254             :  *
     255             :  * @param key the key object to work with
     256             :  * @return the system's UID of the key
     257             :  * @retval (uid_t)-1 on NULL key
     258             :  * @see keyGetGID(), keySetUID(), keyGetOwner()
     259             :  */
     260          36 : uid_t keyGetUID (const Key * key)
     261             : {
     262             :         const char * uid;
     263             :         long int val;
     264             :         char * endptr;
     265          36 :         int errorval = errno;
     266             : 
     267          36 :         if (!key) return (uid_t) -1;
     268             : 
     269          34 :         uid = keyValue (keyGetMeta (key, "uid"));
     270          34 :         if (!uid) return (uid_t) -1;
     271          30 :         if (*uid == '\0') return (uid_t) -1;
     272             : 
     273             :         /*From now on we have to leave using cleanup*/
     274          30 :         errno = 0;
     275          30 :         val = strtol (uid, &endptr, 10);
     276             : 
     277             :         /*Check for errors*/
     278          30 :         if (errno) goto cleanup;
     279             : 
     280             :         /*Check if nothing was found*/
     281          30 :         if (endptr == uid) goto cleanup;
     282             : 
     283             :         /*Check if the whole string was processed*/
     284          26 :         if (*endptr != '\0') goto cleanup;
     285             : 
     286          22 :         return val;
     287             : cleanup:
     288             :         /*First restore errno*/
     289           8 :         errno = errorval;
     290           8 :         return (uid_t) -1;
     291             : }
     292             : 
     293             : 
     294             : /**
     295             :  * Set the user ID of a key.
     296             :  *
     297             :  * @deprecated This API is obsolete.
     298             :  *
     299             :  * See @ref UID for more information about user IDs.
     300             :  *
     301             :  * @param key the key object to work with
     302             :  * @param uid the user ID to set
     303             :  * @retval 0 on success
     304             :  * @retval -1 on NULL key or conversion error
     305             :  * @see keySetGID(), keyGetUID(), keyGetOwner()
     306             :  */
     307          20 : int keySetUID (Key * key, uid_t uid)
     308             : {
     309             :         char str[MAX_LEN_INT];
     310          20 :         if (!key) return -1;
     311             : 
     312          20 :         if (snprintf (str, MAX_LEN_INT - 1, "%d", uid) < 0)
     313             :         {
     314             :                 return -1;
     315             :         }
     316             : 
     317          20 :         keySetMeta (key, "uid", str);
     318             : 
     319          20 :         return 0;
     320             : }
     321             : 
     322             : 
     323             : /**
     324             :  * Get the group ID of a key.
     325             :  *
     326             :  * @deprecated This API is obsolete.
     327             :  *
     328             :  * @section GID GID
     329             :  *
     330             :  * The group ID is a unique identification for every group present on
     331             :  * a system. Keys will belong to root (0) as long as you did not get their
     332             :  * real GID with kdbGet().
     333             :  *
     334             :  * Unlike UID users might change their group. This makes it possible to
     335             :  * share configuration between some users.
     336             :  *
     337             :  * A fresh key will have (gid_t)-1 also known as the group nogroup.
     338             :  * It means that the key is not related to a group ID at the moment.
     339             :  *
     340             :  * @param key the key object to work with
     341             :  * @return the system's GID of the key
     342             :  * @retval (gid_t)-1 on NULL key or currently unknown ID
     343             :  * @see keySetGID(), keyGetUID()
     344             :  */
     345          14 : gid_t keyGetGID (const Key * key)
     346             : {
     347             :         const char * gid;
     348             :         long int val;
     349             :         char * endptr;
     350          14 :         int errorval = errno;
     351             : 
     352          14 :         if (!key) return (gid_t) -1;
     353             : 
     354          12 :         gid = keyValue (keyGetMeta (key, "gid"));
     355          12 :         if (!gid) return (gid_t) -1;
     356          10 :         if (*gid == '\0') return (gid_t) -1;
     357             : 
     358             :         /*From now on we have to leave using cleanup*/
     359          10 :         errno = 0;
     360          10 :         val = strtol (gid, &endptr, 10);
     361             : 
     362             :         /*Check for errors*/
     363          10 :         if (errno) goto cleanup;
     364             : 
     365             :         /*Check if nothing was found*/
     366          10 :         if (endptr == gid) goto cleanup;
     367             : 
     368             :         /*Check if the whole string was processed*/
     369          10 :         if (*endptr != '\0') goto cleanup;
     370             : 
     371          10 :         return val;
     372             : cleanup:
     373             :         /*First restore errno*/
     374           0 :         errno = errorval;
     375           0 :         return (gid_t) -1;
     376             : }
     377             : 
     378             : 
     379             : /**
     380             :  * Set the group ID of a key.
     381             :  *
     382             :  * @deprecated This API is obsolete.
     383             :  *
     384             :  * See @ref GID for more information about group IDs.
     385             :  *
     386             :  * @param key the key object to work with
     387             :  * @param gid is the group ID
     388             :  * @retval 0 on success
     389             :  * @retval -1 on NULL key
     390             :  * @see keyGetGID(), keySetUID()
     391             :  */
     392          12 : int keySetGID (Key * key, gid_t gid)
     393             : {
     394             :         char str[MAX_LEN_INT];
     395          12 :         if (!key) return -1;
     396             : 
     397          12 :         if (snprintf (str, MAX_LEN_INT - 1, "%d", gid) < 0)
     398             :         {
     399             :                 return -1;
     400             :         }
     401             : 
     402          12 :         keySetMeta (key, "gid", str);
     403             : 
     404          12 :         return 0;
     405             : }
     406             : 
     407             : 
     408             : /**
     409             :  * Set mode so that key will be recognized as directory.
     410             :  *
     411             :  * @deprecated This API is obsolete.
     412             :  *
     413             :  * The function will add all executable bits.
     414             :  *
     415             :  * - Mode 0200 will be translated to 0311
     416             :  * - Mode 0400 will be translated to 0711
     417             :  * - Mode 0664 will be translated to 0775
     418             :  *
     419             :  * The macro KDB_DIR_MODE (defined to 0111) will be used for that.
     420             :  *
     421             :  * The executable bits show that child keys are allowed and listable. There
     422             :  * is no way to have child keys which are not listable for anyone, but it is
     423             :  * possible to restrict listing the keys to the owner only.
     424             :  *
     425             :  * - Mode 0000 means that it is a key not read or writable to anyone.
     426             :  * - Mode 0111 means that it is a directory not read or writable to anyone.
     427             :  *   But it is recognized as directory to anyone.
     428             :  *
     429             :  * For more about mode see keySetMode().
     430             :  *
     431             :  * It is not possible to access keys below a not executable key.
     432             :  * If a key is not writeable and executable kdbSet() will fail to access the
     433             :  * keys below.
     434             :  * If a key is not readable and executable kdbGet() will fail to access the
     435             :  * keys below.
     436             :  *
     437             :  * @param key the key to set permissions to be recognized as directory.
     438             :  * @retval 0 on success
     439             :  * @retval -1 on NULL pointer
     440             :  * @see keySetMode()
     441             :  */
     442        1068 : int keySetDir (Key * key)
     443             : {
     444             :         mode_t mode;
     445        1068 :         if (!key) return -1;
     446             : 
     447        1068 :         mode = keyGetMode (key);
     448        1068 :         mode |= KDB_DIR_MODE;
     449        1068 :         keySetMode (key, mode);
     450             : 
     451        1068 :         return 0;
     452             : }
     453             : 
     454             : 
     455             : /**
     456             :  * Return the key mode permissions.
     457             :  *
     458             :  * @deprecated This API is obsolete.
     459             :  *
     460             :  * Default is 0664 (octal) for keys and 0775 for directory keys
     461             :  * which used keySetDir().
     462             :  *
     463             :  * The defaults are defined with the macros KDB_FILE_MODE and KDB_DIR_MODE.
     464             :  *
     465             :  * For more information about the mode permissions see @ref mode.
     466             :  *
     467             :  * @param key the key object to work with
     468             :  * @return mode permissions of the key
     469             :  * @retval KDB_FILE_MODE as defaults
     470             :  * @retval (mode_t)-1 on NULL pointer
     471             :  * @see keySetMode()
     472             :  */
     473        2144 : mode_t keyGetMode (const Key * key)
     474             : {
     475             :         const char * mode;
     476             :         long int val;
     477             :         char * endptr;
     478        2144 :         int errorval = errno;
     479             : 
     480        2144 :         if (!key) return (mode_t) -1;
     481             : 
     482        2142 :         mode = keyValue (keyGetMeta (key, "mode"));
     483        2142 :         if (!mode) return KDB_FILE_MODE;
     484        2098 :         if (*mode == '\0') return KDB_FILE_MODE;
     485             : 
     486             :         /*From now on we have to leave using cleanup*/
     487        2096 :         errno = 0;
     488        2096 :         val = strtol (mode, &endptr, 8);
     489             : 
     490             :         /*Check for errors*/
     491        2096 :         if (errno) goto cleanup;
     492             : 
     493             :         /*Check if nothing was found*/
     494        2096 :         if (endptr == mode) goto cleanup;
     495             : 
     496             :         /*Check if the whole string was processed*/
     497        2092 :         if (*endptr != '\0') goto cleanup;
     498             : 
     499        2088 :         return val;
     500             : cleanup:
     501             :         /*First restore errno*/
     502           8 :         errno = errorval;
     503           8 :         return KDB_FILE_MODE;
     504             : }
     505             : 
     506             : 
     507             : /**
     508             :  * Set the key mode permissions.
     509             :  *
     510             :  * @deprecated This API is obsolete.
     511             :  * It is only a mapping
     512             :  * to keySetMeta(key, "mode", str) which should be preferred.
     513             :  *
     514             :  * The mode consists of 9 individual bits for mode permissions.
     515             :  * In the following explanation the octal notation with leading
     516             :  * zero will be used.
     517             :  *
     518             :  * Default is 0664 (octal) for keys and 0775 for directory keys
     519             :  * which used keySetDir().
     520             :  *
     521             :  * The defaults are defined with the macros KDB_FILE_MODE and KDB_DIR_MODE.
     522             :  *
     523             :  * @note libelektra 0.7.0 only allows 0775 (directory keys) and
     524             :  * 0664 (other keys). More will be added later in a sense of the
     525             :  * description below.
     526             :  *
     527             :  * @section mode Modes
     528             :  *
     529             :  * 0000 is the most restrictive mode. No user might read, write
     530             :  * or execute the key.
     531             :  *
     532             :  * Reading the key means to get the value by kdbGet().
     533             :  *
     534             :  * Writing the key means to set the value by kdbSet().
     535             :  *
     536             :  * Execute the key means to make a step deeper in the hierarchy.
     537             :  * But you must be able to read the key to be able to list the
     538             :  * keys below. See also keySetDir() in that context.
     539             :  * But you must be able to write the key to be able to add or
     540             :  * remove keys below.
     541             :  *
     542             :  * 0777 is the most relaxing mode. Every user is allowed to
     543             :  * read, write and execute the key, if he is allowed to execute
     544             :  * and read all keys below.
     545             :  *
     546             :  * 0700 allows every action for the current user, identified by
     547             :  * the uid. See keyGetUID() and keySetUID().
     548             :  *
     549             :  * To be more specific for the user the single bits can elect
     550             :  * the mode for read, write and execute. 0100 only allows
     551             :  * executing which gives the information that it is a directory
     552             :  * for that user, but not accessible. 0200 only allows reading.
     553             :  * This information may be combined to 0300, which allows execute
     554             :  * and reading of the directory. Last 0400 decides about the
     555             :  * writing permissions.
     556             :  *
     557             :  * The same as above is also valid for the 2 other octal digits.
     558             :  * 0070 decides about the group permissions, in that case full
     559             :  * access. Groups are identified by the gid. See keyGetGID() and
     560             :  * keySetGID(). In that example everyone with a different uid,
     561             :  * but the gid of the the key, has full access.
     562             :  *
     563             :  * 0007 decides about the world permissions. This is taken into
     564             :  * account when neither the uid nor the gid matches. So that
     565             :  * example would allow everyone with a different uid and gid
     566             :  * of that key gains full access.
     567             :  *
     568             :  * @param key the key to set mode permissions
     569             :  * @param mode the mode permissions
     570             :  * @retval 0 on success
     571             :  * @retval -1 on NULL key
     572             :  * @see keyGetMode()
     573             :  */
     574        2106 : int keySetMode (Key * key, mode_t mode)
     575             : {
     576             :         char str[MAX_LEN_INT];
     577        2106 :         if (!key) return -1;
     578             : 
     579        2106 :         if (snprintf (str, MAX_LEN_INT - 1, "%o", mode) < 0)
     580             :         {
     581             :                 return -1;
     582             :         }
     583             : 
     584        2106 :         keySetMeta (key, "mode", str);
     585             : 
     586        2106 :         return 0;
     587             : }
     588             : 
     589             : 
     590             : /*********************************************
     591             :  *    Access times methods                   *
     592             :  *********************************************/
     593             : 
     594             : 
     595             : /**
     596             :  * Get last time the key data was read from disk.
     597             :  *
     598             :  * @deprecated This API is obsolete.
     599             :  *
     600             :  * Every kdbGet() might update the access time
     601             :  * of a key. You get information when the key
     602             :  * was read the last time from the database.
     603             :  *
     604             :  * You will get 0 when the key was not read already.
     605             :  *
     606             :  * Beware that multiple copies of keys with keyDup() might have different
     607             :  * atimes because you kdbGet() one, but not the
     608             :  * other. You can use this information to decide which
     609             :  * key is the latest.
     610             :  *
     611             :  * @param key Key to get information from.
     612             :  * @return the time you got the key with kdbGet()
     613             :  * @retval 0 on key that was never kdbGet()
     614             :  * @retval (time_t)-1 on NULL pointer
     615             :  * @see keySetATime()
     616             :  * @see kdbGet()
     617             :  */
     618          10 : time_t keyGetATime (const Key * key)
     619             : {
     620             :         const char * atime;
     621             :         long int val;
     622             :         char * endptr;
     623          10 :         int errorval = errno;
     624             : 
     625          10 :         if (!key) return (time_t) -1;
     626             : 
     627           8 :         atime = keyValue (keyGetMeta (key, "atime"));
     628           8 :         if (!atime) return 0;
     629           6 :         if (*atime == '\0') return (time_t) -1;
     630             : 
     631             :         /*From now on we have to leave using cleanup*/
     632           6 :         errno = 0;
     633           6 :         val = strtol (atime, &endptr, 10);
     634             : 
     635             :         /*Check for errors*/
     636           6 :         if (errno) goto cleanup;
     637             : 
     638             :         /*Check if nothing was found*/
     639           6 :         if (endptr == atime) goto cleanup;
     640             : 
     641             :         /*Check if the whole string was processed*/
     642           6 :         if (*endptr != '\0') goto cleanup;
     643             : 
     644             :         return val;
     645             : cleanup:
     646             :         /*First restore errno*/
     647           0 :         errno = errorval;
     648           0 :         return (time_t) -1;
     649             : }
     650             : 
     651             : /**
     652             :  * Update the atime information for a key.
     653             :  *
     654             :  * @deprecated This API is obsolete.
     655             :  *
     656             :  * When you do manual sync of keys you might also
     657             :  * update the atime to make them indistinguishable.
     658             :  *
     659             :  * It can also be useful if you work with
     660             :  * keys not using a keydatabase.
     661             :  *
     662             :  * @param key The Key object to work with
     663             :  * @param atime The new access time for the key
     664             :  * @retval 0 on success
     665             :  * @retval -1 on NULL pointer
     666             :  * @see keyGetATime()
     667             :  */
     668           8 : int keySetATime (Key * key, time_t atime)
     669             : {
     670             :         char str[MAX_LEN_INT];
     671           8 :         if (!key) return -1;
     672             : 
     673           6 :         if (snprintf (str, MAX_LEN_INT - 1, "%lu", atime) < 0)
     674             :         {
     675             :                 return -1;
     676             :         }
     677             : 
     678           6 :         keySetMeta (key, "atime", str);
     679             : 
     680           6 :         return 0;
     681             : }
     682             : 
     683             : 
     684             : /**
     685             :  * Get last modification time of the key on disk.
     686             :  *
     687             :  * @deprecated This API is obsolete.
     688             :  *
     689             :  * You will get 0 when the key was not read already.
     690             :  *
     691             :  * Everytime you change value or comment and kdbSet()
     692             :  * the key the mtime will be updated. When you kdbGet()
     693             :  * the key, the atime is set appropriate.
     694             :  *
     695             :  * Not changed keys may not even passed to kdbSet_backend()
     696             :  * so it will not update this time, even after kdbSet().
     697             :  *
     698             :  * It is possible that other keys written to disc
     699             :  * influence this time if the backend is not grained
     700             :  * enough.
     701             :  *
     702             :  * If you add or remove a key the key thereunder
     703             :  * in the hierarchy will update the mtime
     704             :  * if written with kdbSet() to disc.
     705             :  *
     706             :  * @param key Key to get information from.
     707             :  * @see keySetMTime()
     708             :  * @return the last modification time
     709             :  * @retval (time_t)-1 on NULL pointer
     710             :  */
     711          10 : time_t keyGetMTime (const Key * key)
     712             : {
     713             :         const char * mtime;
     714             :         long int val;
     715             :         char * endptr;
     716          10 :         int errorval = errno;
     717             : 
     718          10 :         if (!key) return (time_t) -1;
     719             : 
     720           8 :         mtime = keyValue (keyGetMeta (key, "mtime"));
     721           8 :         if (!mtime) return 0;
     722           6 :         if (*mtime == '\0') return (time_t) -1;
     723             : 
     724             :         /*From now on we have to leave using cleanup*/
     725           6 :         errno = 0;
     726           6 :         val = strtol (mtime, &endptr, 10);
     727             : 
     728             :         /*Check for errors*/
     729           6 :         if (errno) goto cleanup;
     730             : 
     731             :         /*Check if nothing was found*/
     732           6 :         if (endptr == mtime) goto cleanup;
     733             : 
     734             :         /*Check if the whole string was processed*/
     735           6 :         if (*endptr != '\0') goto cleanup;
     736             : 
     737             :         return val;
     738             : cleanup:
     739             :         /*First restore errno*/
     740           0 :         errno = errorval;
     741           0 :         return (time_t) -1;
     742             : }
     743             : 
     744             : /**
     745             :  * Update the mtime information for a key.
     746             :  *
     747             :  * @deprecated This API is obsolete.
     748             :  *
     749             :  * @param key The Key object to work with
     750             :  * @param mtime The new modification time for the key
     751             :  * @retval 0 on success
     752             :  * @see keyGetMTime()
     753             :  */
     754           8 : int keySetMTime (Key * key, time_t mtime)
     755             : {
     756             :         char str[MAX_LEN_INT];
     757           8 :         if (!key) return -1;
     758             : 
     759           6 :         if (snprintf (str, MAX_LEN_INT - 1, "%lu", mtime) < 0)
     760             :         {
     761             :                 return -1;
     762             :         }
     763             : 
     764           6 :         keySetMeta (key, "mtime", str);
     765             : 
     766           6 :         return 0;
     767             : }
     768             : 
     769             : 
     770             : /**
     771             :  * Get last time the key metadata was changed from disk.
     772             :  *
     773             :  * @deprecated This API is obsolete.
     774             :  *
     775             :  * You will get 0 when the key was not read already.
     776             :  *
     777             :  * Any changed field in metadata will influence the
     778             :  * ctime of a key.
     779             :  *
     780             :  * This time is not updated if only value
     781             :  * or comment are changed.
     782             :  *
     783             :  * Not changed keys will not update this time,
     784             :  * even after kdbSet().
     785             :  *
     786             :  * It is possible that other keys written to disc
     787             :  * influence this time if the backend is not grained
     788             :  * enough.
     789             :  *
     790             :  * @param key Key to get information from.
     791             :  * @see keySetCTime()
     792             :  * @retval (time_t)-1 on NULL pointer
     793             :  * @return the metadata change time
     794             :  */
     795          10 : time_t keyGetCTime (const Key * key)
     796             : {
     797             :         const char * ctime;
     798             :         long int val;
     799             :         char * endptr;
     800          10 :         int errorval = errno;
     801             : 
     802          10 :         if (!key) return (time_t) -1;
     803             : 
     804           8 :         ctime = keyValue (keyGetMeta (key, "ctime"));
     805           8 :         if (!ctime) return 0;
     806           6 :         if (*ctime == '\0') return (time_t) -1;
     807             : 
     808             :         /*From now on we have to leave using cleanup*/
     809           6 :         errno = 0;
     810           6 :         val = strtol (ctime, &endptr, 10);
     811             : 
     812             :         /*Check for errors*/
     813           6 :         if (errno) goto cleanup;
     814             : 
     815             :         /*Check if nothing was found*/
     816           6 :         if (endptr == ctime) goto cleanup;
     817             : 
     818             :         /*Check if the whole string was processed*/
     819           6 :         if (*endptr != '\0') goto cleanup;
     820             : 
     821             :         return val;
     822             : cleanup:
     823             :         /*First restore errno*/
     824           0 :         errno = errorval;
     825           0 :         return (time_t) -1;
     826             : }
     827             : 
     828             : 
     829             : /**
     830             :  * Update the ctime information for a key.
     831             :  *
     832             :  * @deprecated This API is obsolete.
     833             :  *
     834             :  * @param key The Key object to work with
     835             :  * @param ctime The new change metadata time for the key
     836             :  * @retval 0 on success
     837             :  * @retval -1 on NULL pointer
     838             :  * @see keyGetCTime()
     839             :  */
     840           8 : int keySetCTime (Key * key, time_t ctime)
     841             : {
     842             :         char str[MAX_LEN_INT];
     843           8 :         if (!key) return -1;
     844             : 
     845           6 :         if (snprintf (str, MAX_LEN_INT - 1, "%lu", ctime) < 0)
     846             :         {
     847             :                 return -1;
     848             :         }
     849             : 
     850           6 :         keySetMeta (key, "ctime", str);
     851             : 
     852           6 :         return 0;
     853             : }
     854             : 
     855             : #endif
     856             : 
     857             : /**
     858             :  * Compare the order metadata of two keys.
     859             :  *
     860             :  * @return a number less than, equal to or greater than zero if
     861             :  *    the order of k1 is found, respectively, to be less than,
     862             :  *    to match, or be greater than the order of k2. If one key is
     863             :  *    NULL, but the other isn't, the key which is not NULL is considered
     864             :  *    to be greater. If both keys are NULL, they are
     865             :  *    considered to be equal. If one key does have an order
     866             :  *    metadata but the other has not, the key with the metadata
     867             :  *    is considered greater. If no key has metadata,
     868             :  *    they are considered to be equal.
     869             :  *
     870             :  * @param ka key to compare with
     871             :  * @param kb other key to compare with
     872             :  */
     873         520 : int elektraKeyCmpOrder (const Key * ka, const Key * kb)
     874             : {
     875             : 
     876         520 :         if (!ka && !kb) return 0;
     877             : 
     878         518 :         if (ka && !kb) return 1;
     879             : 
     880         516 :         if (!ka && kb) return -1;
     881             : 
     882         514 :         int aorder = -1;
     883         514 :         int border = -1;
     884             : 
     885         514 :         const Key * kam = keyGetMeta (ka, "order");
     886         514 :         const Key * kbm = keyGetMeta (kb, "order");
     887             : 
     888         952 :         if (kam) aorder = atoi (keyString (kam));
     889         971 :         if (kbm) border = atoi (keyString (kbm));
     890             : 
     891         514 :         if (aorder > 0 && border > 0) return aorder - border;
     892             : 
     893         100 :         if (aorder < 0 && border < 0) return 0;
     894             : 
     895          67 :         if (aorder < 0 && border >= 0) return -1;
     896             : 
     897          24 :         if (aorder >= 0 && border < 0) return 1;
     898             : 
     899             :         /* cannot happen anyway */
     900           0 :         return 0;
     901             : }
     902             : 
     903             : 
     904             : /**
     905             :  * creates an metadata array or appends another element to an existing metadata array
     906             :  * e.g.
     907             :  * Key *key = keyNew("user/test", KEY_END);
     908             :  * elektraMetaArrayAdd(key, "test", "val0");
     909             :  * key now has "test/#0" with value "val0" as metadata
     910             :  * elektraMetaArrayAdd(key, "test", "val1");
     911             :  * appends "test/#1" with value "val1" to key
     912             :  *
     913             :  * @param key the key the metadata should be added to
     914             :  * @param metaName the name of the metakey array parent
     915             :  * @param value the value of the newly appended metakey
     916             :  */
     917             : 
     918        1487 : void elektraMetaArrayAdd (Key * key, const char * metaName, const char * value)
     919             : {
     920        1487 :         const Key * meta = keyGetMeta (key, metaName);
     921             :         Key * arrayKey;
     922        1487 :         if (!meta)
     923             :         {
     924         762 :                 keySetMeta (key, metaName, "#0");
     925         762 :                 arrayKey = keyDup (keyGetMeta (key, metaName));
     926         762 :                 keySetString (arrayKey, 0);
     927         762 :                 keyAddBaseName (arrayKey, "#");
     928             :         }
     929             :         else
     930             :         {
     931         725 :                 arrayKey = keyDup (meta);
     932         725 :                 keyAddBaseName (arrayKey, keyString (meta));
     933             :         }
     934        1487 :         elektraArrayIncName (arrayKey);
     935        1487 :         keySetMeta (key, keyName (arrayKey), value);
     936        1487 :         keySetMeta (key, metaName, keyBaseName (arrayKey));
     937        1487 :         keyDel (arrayKey);
     938        1487 : }
     939             : 
     940             : /**
     941             :  * Create a `KeySet` from a metakey array.
     942             :  *
     943             :  * For example, the following function call
     944             :  *
     945             :  * @code
     946             : elektraMetaArrayToKS(
     947             :         keyNew ("/a", KEY_VALUE, "b, c",
     948             :                 KEY_META, "dep",    "#1",
     949             :                 KEY_META, "dep/#0", "/b",
     950             :                 KEY_META, "dep/#1", "/c", KEY_END),
     951             :         "dep");
     952             :  * @endcode
     953             :  *
     954             :  * returns a `KeySet` containing the keys `dep` with value `#1`, `"dep/#0"` with value `"/b"` and
     955             :  * `"dep/#1"` with value `"/c"`.
     956             :  *
     957             :  * If no meta key array is found, null is returned.
     958             :  * The returned `KeySet` must be freed with `ksDel`
     959             :  *
     960             :  * @returns a keyset containing all the metakeys of the metakey array
     961             :  * @param key the key containing the metakey array
     962             :  * @param metaName the name of the metakey array parent
     963             :  */
     964        4832 : KeySet * elektraMetaArrayToKS (const Key * key, const char * metaName)
     965             : {
     966        4832 :         const Key * meta = keyGetMeta (key, metaName);
     967        4832 :         if (!meta) return NULL;
     968             : 
     969         862 :         KeySet * result = ksNew (0, KS_END);
     970             : 
     971         862 :         if (keyString (meta)[0] != '#')
     972             :         {
     973           0 :                 ksAppendKey (result, (Key *) meta);
     974           0 :                 ksRewind (result);
     975           0 :                 return result;
     976             :         }
     977         862 :         ksAppendKey (result, keyDup (meta));
     978         862 :         Key * currentKey = keyDup (meta);
     979         862 :         keyAddName (currentKey, "#");
     980         862 :         elektraArrayIncName (currentKey);
     981         862 :         Key * curMeta = NULL;
     982        3367 :         while ((curMeta = (Key *) keyGetMeta (key, keyName (currentKey))) != NULL)
     983             :         {
     984        1643 :                 ksAppendKey (result, keyDup (curMeta));
     985        1643 :                 elektraArrayIncName (currentKey);
     986             :         }
     987         862 :         keyDel (currentKey);
     988         862 :         ksRewind (result);
     989         862 :         return result;
     990             : }
     991             : 
     992             : 
     993             : /**
     994             :  * @internal
     995             :  *
     996             :  * elektraSortTopology helper
     997             :  * matrix struct
     998             :  */
     999             : typedef struct
    1000             : {
    1001             :         Key * key;
    1002             :         kdb_octet_t isResolved;
    1003             :         unsigned long * deps;
    1004             : } _adjMatrix;
    1005             : 
    1006             : /**
    1007             :  * @internal
    1008             :  *
    1009             :  * elektraSortTopology helper
    1010             :  * ordering function for qsort
    1011             :  */
    1012        4672 : static int topCmpOrder (const void * a, const void * b)
    1013             : {
    1014        4672 :         const Key * ka = (*(const Key **) a);
    1015        4672 :         const Key * kb = (*(const Key **) b);
    1016             : 
    1017        4672 :         if (!ka && !kb) return 0;
    1018        4672 :         if (ka && !kb) return 1;
    1019        4672 :         if (!ka && kb) return -1;
    1020             : 
    1021        4672 :         const Key * kam = keyGetMeta (ka, "order");
    1022        4672 :         const Key * kbm = keyGetMeta (kb, "order");
    1023             : 
    1024        4672 :         return strcmp (keyString (kam), keyString (kbm));
    1025             : }
    1026             : 
    1027             : 
    1028             : /**
    1029             :  * @internal
    1030             :  *
    1031             :  * elektraSortTopology helper
    1032             :  * returns the index of dependency depKey
    1033             :  */
    1034         401 : static int getArrayIndex (Key * depKey, _adjMatrix * adjMatrix, size_t size)
    1035             : {
    1036        1083 :         for (unsigned int i = 0; i < size; ++i)
    1037             :         {
    1038        1079 :                 if (!strcmp (keyName (adjMatrix[i].key), keyString (depKey))) return i;
    1039             :         }
    1040             :         return -1;
    1041             : }
    1042             : /**
    1043             :  * @internal
    1044             :  *
    1045             :  * elektraSortTopology helper
    1046             :  * removes resolved dependency j from our matrix
    1047             :  */
    1048             : static int resolveDep (unsigned int j, _adjMatrix * adjMatrix, size_t size)
    1049             : {
    1050        2824 :         int removed = 0;
    1051       11081 :         for (unsigned int i = 0; i < size; ++i)
    1052             :         {
    1053        8257 :                 if (adjMatrix[i].deps[j])
    1054             :                 {
    1055         125 :                         ++removed;
    1056         125 :                         adjMatrix[i].deps[j] = 0;
    1057             :                 }
    1058             :         }
    1059             :         return removed;
    1060             : }
    1061             : 
    1062             : /**
    1063             :  * @internal
    1064             :  *
    1065             :  * elektraSortTopology helper
    1066             :  * checks if the key with index j has unresolved dependencies
    1067             :  */
    1068             : static int hasUnresolvedDependencies (unsigned int j, _adjMatrix * adjMatrix, size_t size)
    1069             : {
    1070         362 :         for (unsigned int i = 0; i < size; ++i)
    1071             :         {
    1072         328 :                 if (adjMatrix[j].deps[i]) return 1;
    1073             :         }
    1074             :         return 0;
    1075             : }
    1076             : 
    1077             : /**
    1078             :  * @internal
    1079             :  *
    1080             :  * elektraSortTopology helper
    1081             :  * resolve all dependencies of the key with the index j in our matrix.
    1082             :  */
    1083          73 : static int resolveDeps (unsigned int j, _adjMatrix * adjMatrix, size_t size, KeySet * done, Key * orderCounter)
    1084          73 : {
    1085          73 :         unsigned int loops = 0;
    1086          73 :         unsigned int frontier[size];
    1087          73 :         unsigned int todo = 0;
    1088         340 :         for (unsigned int i = 0; i < size; ++i)
    1089             :         {
    1090         267 :                 if (adjMatrix[j].deps[i])
    1091             :                 {
    1092           2 :                         frontier[i] = 1;
    1093           2 :                         ++todo;
    1094             :                 }
    1095             :                 else
    1096             :                 {
    1097         265 :                         frontier[i] = 0;
    1098             :                 }
    1099             :         }
    1100             :         int found = 1;
    1101             : 
    1102             :         // loop until all dependencies are added to frontier
    1103         146 :         while (found)
    1104             :         {
    1105             :                 found = 0;
    1106         267 :                 for (unsigned int i = 0; i < size; ++i)
    1107             :                 {
    1108         267 :                         if (!frontier[i]) continue;
    1109           2 :                         if (hasUnresolvedDependencies (i, adjMatrix, size))
    1110             :                         {
    1111           0 :                                 for (unsigned int k = 0; k < size; ++k)
    1112             :                                 {
    1113           0 :                                         if (adjMatrix[i].deps[k])
    1114             :                                         {
    1115           0 :                                                 if (!frontier[k])
    1116             :                                                 {
    1117           0 :                                                         found = 1;
    1118           0 :                                                         ++todo;
    1119           0 :                                                         frontier[k] = 1;
    1120             :                                                 }
    1121             :                                         }
    1122             :                                 }
    1123             :                         }
    1124             :                 }
    1125             :         }
    1126          73 :         if (todo == 0)
    1127             :         {
    1128             :                 // all dependencies are already resolved, give key an order number and add it to
    1129             :                 // the our list of resolved keys (done)
    1130          71 :                 adjMatrix[j].isResolved = 1;
    1131          71 :                 resolveDep (j, adjMatrix, size);
    1132          71 :                 keySetMeta (adjMatrix[j].key, "order", keyBaseName (orderCounter));
    1133          71 :                 elektraArrayIncName (orderCounter);
    1134          71 :                 ksAppendKey (done, keyDup (adjMatrix[j].key));
    1135          71 :                 return 1;
    1136             :         }
    1137             :         unsigned int max_loops = todo;
    1138           8 :         for (unsigned int i = 0; todo; ++i)
    1139             :         {
    1140           8 :                 if (i == size)
    1141             :                 {
    1142           0 :                         ++loops;
    1143           0 :                         i = 0;
    1144             :                 }
    1145           8 :                 if (loops > max_loops) return -1; // more loops than we had unresolved keys -> cycle
    1146           8 :                 if (!frontier[i]) continue;
    1147           2 :                 if (!hasUnresolvedDependencies (i, adjMatrix, size))
    1148             :                 {
    1149           2 :                         resolveDep (i, adjMatrix, size);
    1150           2 :                         frontier[i] = 0;
    1151           2 :                         --todo;
    1152           2 :                         adjMatrix[i].isResolved = 1;
    1153           2 :                         resolveDep (i, adjMatrix, size);
    1154           2 :                         keySetMeta (adjMatrix[i].key, "order", keyBaseName (orderCounter));
    1155           2 :                         elektraArrayIncName (orderCounter);
    1156           2 :                         ksAppendKey (done, keyDup (adjMatrix[i].key));
    1157             :                 }
    1158             :         }
    1159             :         return 1;
    1160             : }
    1161             : 
    1162             : /**
    1163             :  * elektraSortTopology helper
    1164             :  * tests if name is a valid keyname
    1165             :  */
    1166         401 : static int isValidKeyName (const char * testName)
    1167             : {
    1168         401 :         int retVal = 0;
    1169         401 :         Key * testKey = keyNew (testName, KEY_CASCADING_NAME, KEY_END);
    1170         401 :         if (!strcmp (keyName (testKey), testName)) retVal = 1;
    1171         401 :         keyDel (testKey);
    1172         401 :         return retVal;
    1173             : }
    1174             : 
    1175             : /**
    1176             :  * @brief topological sorting
    1177             :  *
    1178             :  * @param array the array where the sorted keys will be stored in topological order.
    1179             :  *        Nothing will be written into an array if
    1180             :  * @param ks is the keyset that should be sorted.
    1181             :  *        Dependencies and order is defined by metakeys.
    1182             :  *
    1183             :  * - the "dep/#" metakeys
    1184             :  *  e.g. the Key *k = keyNew ("/a", KEY_VALUE, "b, c",
    1185             :  *  KEY_META, "dep", "#1", KEY_META, "dep/#0", "/b", KEY_META, "dep/#1", "/c", KEY_END), "dep");
    1186             :  *  depends on Key "/b" and Key "/c".
    1187             :  * - if "order" metakeys are defined for the keys the algorithm tries to resolves them by that
    1188             :  *  order using lexical comparison. You should prefer `#0` array syntax.
    1189             :  *
    1190             :  * Duplicated and reflexive dep entries are ignored.
    1191             :  *
    1192             :  * The algorithm used is a mixture of Kahn and BFS.
    1193             :  * Furthermore the algorithm does not use recursion.
    1194             :  *
    1195             :  * First a BFS with the keys sorted by "order" is used.
    1196             :  * Then all dependencies (recursively) of every key is collected.
    1197             :  *
    1198             :  * @retval 1 on success
    1199             :  * @retval 0 for cycles
    1200             :  * @retval -1 for invalid dependencies
    1201             :  */
    1202             : 
    1203        1256 : int elektraSortTopology (KeySet * ks, Key ** array)
    1204        1256 : {
    1205        1256 :         if (ks == NULL || array == NULL) return -1;
    1206        1256 :         KeySet * done = ksNew (0, KS_END);
    1207        1256 :         ksRewind (ks);
    1208             :         Key * cur;
    1209        1256 :         ssize_t size = ksGetSize (ks);
    1210        1256 :         Key * orderCounter = keyNew ("/#", KEY_CASCADING_NAME, KEY_END);
    1211        1256 :         elektraArrayIncName (orderCounter);
    1212        1256 :         _adjMatrix adjMatrix[size];
    1213        1256 :         int i = 0;
    1214        1256 :         int retVal = 1;
    1215        1256 :         int depCount = 0;
    1216        1256 :         Key ** localArray = elektraMalloc (size * sizeof (Key *));
    1217        1256 :         elektraKsToMemArray (ks, localArray);
    1218        1256 :         qsort (localArray, size, sizeof (Key *), topCmpOrder);
    1219        4207 :         for (long j = 0; j < size; ++j)
    1220             :         {
    1221        2951 :                 adjMatrix[j].key = localArray[j];
    1222        2951 :                 adjMatrix[j].isResolved = 0;
    1223        2951 :                 adjMatrix[j].deps = elektraCalloc (sizeof (unsigned long) * size);
    1224             :         }
    1225        1256 :         kdb_octet_t hasOrder = 0;
    1226        1256 :         if (keyGetMeta (localArray[0], "order")) hasOrder = 1;
    1227        1256 :         unsigned int unresolved = 0;
    1228        4203 :         for (int j = 0; j < size; ++j)
    1229             :         {
    1230        2951 :                 cur = localArray[j];
    1231        2951 :                 KeySet * deps = elektraMetaArrayToKS (cur, "dep");
    1232        2951 :                 keyDel (ksLookupByName (deps, "dep", KDB_O_POP));
    1233             :                 Key * tmpDep;
    1234        2951 :                 switch (ksGetSize (deps))
    1235             :                 {
    1236             :                 case -1:
    1237             :                 {
    1238             :                         // key has no dependencies, give it an order number and add it to list of resolved dependencies
    1239        2711 :                         keySetMeta (cur, "order", keyBaseName (orderCounter));
    1240        2711 :                         elektraArrayIncName (orderCounter);
    1241        2711 :                         ksAppendKey (done, keyDup (cur));
    1242        2711 :                         adjMatrix[j].isResolved = 1;
    1243        2711 :                         ksDel (deps);
    1244        2711 :                         break;
    1245             :                 }
    1246             :                 case 1:
    1247             :                 {
    1248             :                         // only 1 dependency:
    1249             :                         // test if it's reflexive
    1250         122 :                         tmpDep = ksHead (deps);
    1251         122 :                         if (!strcmp (keyName (cur), keyString (tmpDep)))
    1252             :                         {
    1253          10 :                                 keySetMeta (cur, "order", keyBaseName (orderCounter));
    1254          10 :                                 elektraArrayIncName (orderCounter);
    1255          10 :                                 ksAppendKey (done, keyDup (cur));
    1256          10 :                                 adjMatrix[j].isResolved = 1;
    1257          10 :                                 ksDel (deps);
    1258          10 :                                 break;
    1259             :                         }
    1260             :                         // if not, fallthrough to normal dependency handling
    1261             :                 }
    1262             :                 // FALLTHROUGH
    1263             :                 default:
    1264             :                 {
    1265             :                         int gotUnresolved = 0;
    1266         627 :                         while ((tmpDep = ksNext (deps)) != NULL)
    1267             :                         {
    1268         401 :                                 if (!isValidKeyName (keyString (tmpDep)))
    1269             :                                 {
    1270             :                                         // invalid keyname -> ERROR
    1271             :                                         retVal = -1;
    1272             :                                         break;
    1273             :                                 }
    1274         401 :                                 i = getArrayIndex (tmpDep, adjMatrix, size);
    1275         401 :                                 if (i == -1)
    1276             :                                 {
    1277             :                                         // key doesn't exist yet but has valid name, ignore it.
    1278           4 :                                         continue;
    1279             :                                 }
    1280         397 :                                 else if (i == j)
    1281             :                                 {
    1282             :                                         // reflexiv dependency, do nothing
    1283             :                                 }
    1284             :                                 else
    1285             :                                 {
    1286         387 :                                         if (!adjMatrix[i].isResolved)
    1287             :                                         {
    1288             :                                                 // unresolved dependency
    1289         203 :                                                 adjMatrix[j].deps[i] = 1;
    1290         203 :                                                 ++gotUnresolved;
    1291             :                                                 // simple cycle detection
    1292         203 :                                                 if (adjMatrix[i].deps[j])
    1293             :                                                 {
    1294             :                                                         retVal = 0;
    1295             :                                                         break;
    1296             :                                                 }
    1297             :                                         }
    1298             :                                 }
    1299             :                         }
    1300         230 :                         if (gotUnresolved)
    1301             :                         {
    1302         175 :                                 adjMatrix[j].isResolved = 0;
    1303         175 :                                 ++unresolved;
    1304             :                                 // count unresolved dependencies
    1305         175 :                                 depCount += gotUnresolved;
    1306             :                         }
    1307         230 :                         ksDel (deps);
    1308         230 :                         break;
    1309             :                 }
    1310             :                 }
    1311        2951 :                 if (retVal <= 0) break;
    1312             :         }
    1313        1256 :         if (retVal <= 0)
    1314             :         {
    1315             :                 // error or cycle: goto cleanup
    1316             :                 goto TopSortCleanup;
    1317             :         }
    1318             : 
    1319             :         // resolve all dependencies that can be resolved immediately
    1320        2939 :         for (int j = 0; j < size; ++j)
    1321             :         {
    1322        5658 :                 if (adjMatrix[j].isResolved) depCount -= resolveDep (j, adjMatrix, size);
    1323             :         }
    1324             : 
    1325        1252 :         ssize_t resolved = ksGetSize (done);
    1326        1252 :         if (((depCount + resolved) >= size) && (unresolved))
    1327             :         {
    1328             :                 // more dependencies dependencies than keys:
    1329             :                 //  cycle found !
    1330             :                 retVal = 0;
    1331             :                 goto TopSortCleanup;
    1332             :         }
    1333             : 
    1334        1240 :         if (unresolved)
    1335             :         {
    1336             :                 int found = 1;
    1337             :                 // we have unresolved dependencies
    1338         939 :                 for (int j = 0; j < size + 1; ++j)
    1339             :                 {
    1340             :                         // loop until no dependency can be resolved anymore
    1341        1016 :                         if (j == size)
    1342             :                         {
    1343         168 :                                 if (found)
    1344             :                                 {
    1345          91 :                                         found = 0;
    1346          91 :                                         j = -1;
    1347          91 :                                         unresolved = 0;
    1348          91 :                                         continue;
    1349             :                                 }
    1350             :                                 else
    1351             :                                         break;
    1352             :                         }
    1353         848 :                         if (adjMatrix[j].isResolved) continue;
    1354         129 :                         ++unresolved;
    1355         129 :                         if (hasOrder)
    1356             :                         {
    1357             :                                 // resolve by order
    1358          73 :                                 int ret = resolveDeps (j, adjMatrix, size, done, orderCounter);
    1359          73 :                                 if (ret == -1) break;
    1360          73 :                                 j = -1;
    1361          73 :                                 found = 1;
    1362          73 :                                 continue;
    1363             :                         }
    1364             :                         else
    1365             :                         {
    1366             :                                 // resolve next possible dependency in keyset
    1367         112 :                                 if (!hasUnresolvedDependencies (j, adjMatrix, size))
    1368             :                                 {
    1369          34 :                                         adjMatrix[j].isResolved = 1;
    1370          68 :                                         resolveDep (j, adjMatrix, size);
    1371          34 :                                         keySetMeta (localArray[j], "order", keyBaseName (orderCounter));
    1372          34 :                                         elektraArrayIncName (orderCounter);
    1373          34 :                                         ksAppendKey (done, keyDup (localArray[j]));
    1374          34 :                                         found = 1;
    1375             :                                 }
    1376             :                         }
    1377             :                 }
    1378             :         }
    1379        1240 :         if (unresolved == 0)
    1380             :         {
    1381             :                 // everything resolved
    1382             :                 // add dependencies in topological order to array
    1383        1240 :                 elektraKsToMemArray (ks, array);
    1384        1240 :                 qsort (array, size, sizeof (Key *), topCmpOrder);
    1385        1240 :                 retVal = 1;
    1386             :         }
    1387             :         else
    1388             :         {
    1389             :                 // still unresolved dependencies left:
    1390             :                 // there must be a cycle somewhere
    1391             :                 retVal = 0;
    1392             :         }
    1393             : TopSortCleanup:
    1394        1256 :         ksDel (done);
    1395        1256 :         keyDel (orderCounter);
    1396        1256 :         elektraFree (localArray);
    1397        4207 :         for (ssize_t j = 0; j < size; ++j)
    1398             :         {
    1399        2951 :                 elektraFree (adjMatrix[j].deps);
    1400             :         }
    1401             :         return retVal;
    1402             : }
    1403             : 
    1404             : /**
    1405             :  * returns the metakey array as a string separated by delim
    1406             :  *
    1407             :  * @param key the key containing the metakey array
    1408             :  * @param metaName the name of the metakey array parent
    1409             :  * @param delim delimiter for the records in the returned string
    1410             :  *
    1411             :  * @returns a string containing all metakey values separated by "delim"
    1412             :  */
    1413             : 
    1414         164 : char * elektraMetaArrayToString (const Key * key, const char * metaName, const char * delim)
    1415             : {
    1416         164 :         char * result = NULL;
    1417         164 :         Key * lookupElem = keyDup (keyGetMeta (key, metaName));
    1418         164 :         keyAddBaseName (lookupElem, "#0");
    1419         164 :         Key * elem = (Key *) keyGetMeta (key, keyName (lookupElem));
    1420         164 :         if (elem != NULL)
    1421             :         {
    1422         164 :                 elektraRealloc ((void **) &result, keyGetValueSize (elem));
    1423         164 :                 snprintf (result, keyGetValueSize (elem), "%s", keyString (elem));
    1424             :         }
    1425         164 :         elektraArrayIncName (lookupElem);
    1426         164 :         elem = (Key *) keyGetMeta (key, keyName (lookupElem));
    1427         328 :         while (elem != NULL)
    1428             :         {
    1429           0 :                 elektraRealloc ((void **) &result,
    1430           0 :                                 elektraStrLen (result) + keyGetValueSize (elem) + 1); // String (incl. +2 times \0) + delimiter + whitespace
    1431           0 :                 strcat (result, delim);
    1432           0 :                 strcat (result, keyString (elem));
    1433           0 :                 elektraArrayIncName (lookupElem);
    1434           0 :                 elem = (Key *) keyGetMeta (key, keyName (lookupElem));
    1435             :         }
    1436         164 :         keyDel (lookupElem);
    1437         164 :         return result;
    1438             : }
    1439             : 
    1440             : /**
    1441             :  * @}
    1442             :  */

Generated by: LCOV version 1.13