LCOV - code coverage report
Current view: top level - src/libs/ease - array.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 66 72 91.7 %
Date: 2019-09-12 12:28:41 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Array methods.
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  */
       8             : 
       9             : #define __STDC_FORMAT_MACROS
      10             : 
      11             : #include <kdb.h>
      12             : #include <kdbease.h>
      13             : #include <kdbhelper.h>
      14             : #include <kdbtypes.h>
      15             : 
      16             : #include <ctype.h>
      17             : #include <errno.h>
      18             : #include <limits.h>
      19             : #include <stdio.h>
      20             : #include <stdlib.h>
      21             : #include <string.h>
      22             : 
      23             : /**
      24             :  * @brief validate array syntax
      25             :  *
      26             :  * @param key an element of an array
      27             :  *
      28             :  * @retval -1 if no array element/syntax error/no key
      29             :  * @retval 0 if start
      30             :  * @retval 1 if array element
      31             :  */
      32        8638 : int elektraArrayValidateName (const Key * key)
      33             : {
      34        8638 :         if (!key) return -1;
      35        8638 :         int offsetIndex = elektraArrayValidateBaseNameString (keyBaseName (key));
      36        8638 :         return offsetIndex >= 1 ? 1 : offsetIndex;
      37             : }
      38             : 
      39             : /**
      40             :  * @brief validate array syntax
      41             :  *
      42             :  * @param baseName the supposed array element basename
      43             :  *
      44             :  * @retval -1 if no array element/syntax error/no key
      45             :  * @retval 0 if start
      46             :  * @retval offsetIndex otherwise, where `offsetIndex` stores the offset
      47             :  *                     to the first digit of the array index of `baseName`
      48             :  */
      49      176591 : int elektraArrayValidateBaseNameString (const char * baseName)
      50             : {
      51      176591 :         const char * current = baseName;
      52      176591 :         if (!current || *current != '#') return -1;
      53      171879 :         if (!strcmp (current, "#")) return 0;
      54             : 
      55      168564 :         current++;
      56      168564 :         int underscores = 0;
      57      168564 :         int digits = 0;
      58             : 
      59      416331 :         while (*current == '_')
      60             :         {
      61       79203 :                 current++;
      62       79203 :                 underscores++;
      63             :         }
      64             : 
      65      416360 :         while (isdigit ((unsigned char) *current))
      66             :         {
      67      247796 :                 current++;
      68      247796 :                 digits++;
      69             :         }
      70             : 
      71      168564 :         if (underscores != digits - 1) return -1;
      72      168553 :         if (underscores + digits > ELEKTRA_MAX_ARRAY_SIZE - 2)
      73             :         {
      74             :                 return -1;
      75             :         }
      76             : 
      77      168553 :         return underscores + 1;
      78             : }
      79             : 
      80      185697 : int elektraReadArrayNumber (const char * baseName, kdb_long_long_t * oldIndex)
      81             : {
      82             : 
      83      185697 :         int errnosave = errno;
      84      185697 :         errno = 0;
      85      185697 :         if (sscanf (baseName, ELEKTRA_LONG_LONG_F, oldIndex) != 1)
      86             :         {
      87           0 :                 errno = errnosave;
      88           0 :                 return -1;
      89             :         }
      90             : 
      91      185697 :         if (errno != 0) // any error
      92             :         {
      93           0 :                 errno = errnosave;
      94           0 :                 return -1;
      95             :         }
      96             : 
      97      185697 :         if (*oldIndex < 0) // underflow
      98             :         {
      99             :                 return -1;
     100             :         }
     101             : 
     102             :         /*
     103             :         overflow not possible, cannot be larger than largest number
     104             :         if (*oldIndex >= INT64_MAX) // overflow
     105             :         {
     106             :                 return -1;
     107             :         }
     108             :         */
     109      185697 :         return 0;
     110             : }
     111             : 
     112             : 
     113             : /**
     114             :  * @brief Increment the name of the key by one
     115             :  *
     116             :  * Alphabetical order will remain
     117             :  *
     118             :  * e.g. user/abc/\#9 will be changed to
     119             :  *      user/abc/\#_10
     120             :  *
     121             :  * For the start:
     122             :  *      user/abc/\#
     123             :  * will be changed to
     124             :  *      user/abc/\#0
     125             :  *
     126             :  * @param key which base name will be incremented
     127             :  *
     128             :  * @retval -1 on error (e.g. array too large, non-valid array)
     129             :  * @retval 0 on success
     130             :  */
     131      166041 : int elektraArrayIncName (Key * key)
     132             : {
     133      166041 :         const char * baseName = keyBaseName (key);
     134             : 
     135      166041 :         int offsetIndex = elektraArrayValidateBaseNameString (baseName);
     136      166041 :         if (offsetIndex == -1) return -1;
     137             : 
     138             :         // Jump to array index
     139      166023 :         baseName += offsetIndex;
     140             : 
     141      166023 :         kdb_long_long_t oldIndex = 0;
     142      166023 :         if (offsetIndex && elektraReadArrayNumber (baseName, &oldIndex) == -1) return -1;
     143      166023 :         kdb_long_long_t newIndex = offsetIndex ? oldIndex + 1 : 0; // we increment by one or use 0 if the name contains no index yet
     144             : 
     145             :         char newName[ELEKTRA_MAX_ARRAY_SIZE];
     146             : 
     147      166023 :         elektraWriteArrayNumber (newName, newIndex);
     148      166023 :         keySetBaseName (key, newName);
     149             : 
     150      166023 :         return 0;
     151             : }
     152             : 
     153             : /**
     154             :  * @brief Decrement the name of an array key by one.
     155             :  *
     156             :  * The alphabetical order will remain intact. For example,
     157             :  * `user/abc/\#_10` will be changed to `user/abc/\#9`.
     158             :  *
     159             :  * @param This parameter determines the key name this function decrements.
     160             :  *
     161             :  * @retval -1 on error (e.g. new array index too small, non-valid array)
     162             :  * @retval 0 on success
     163             :  */
     164         126 : int elektraArrayDecName (Key * key)
     165             : {
     166         126 :         const char * baseName = keyBaseName (key);
     167             : 
     168         126 :         int offsetIndex = elektraArrayValidateBaseNameString (baseName);
     169         126 :         if (offsetIndex == -1) return -1;
     170             : 
     171             :         // Jump to array index
     172         126 :         baseName += offsetIndex;
     173             : 
     174         126 :         kdb_long_long_t oldIndex = 0;
     175         126 :         if (elektraReadArrayNumber (baseName, &oldIndex) == -1 || oldIndex == 0) return -1;
     176             : 
     177             :         char newName[ELEKTRA_MAX_ARRAY_SIZE];
     178         124 :         elektraWriteArrayNumber (newName, oldIndex - 1);
     179         124 :         keySetBaseName (key, newName);
     180             : 
     181         124 :         return 0;
     182             : }
     183             : 
     184             : /**
     185             :  * @internal
     186             :  *
     187             :  * Returns true (1) for all keys that are part of the array
     188             :  * identified by the supplied array parent. Only the array
     189             :  * elements themselves, but no subkeys of them will be filtered
     190             :  *
     191             :  * @pre The supplied argument has to be of type (const Key *)
     192             :  * and is the parent of the array to be extracted. For example
     193             :  * if the keys of the array comment/# are to be extracted, a key
     194             :  * with the name "comment" has to be supplied
     195             :  *
     196             :  * @param key the key to be checked against the array
     197             :  * @param argument the array parent
     198             :  * @return 1 if the key is part of the array identified by the
     199             :  * array parent, 0 otherwise
     200             :  *
     201             :  */
     202       16349 : static int arrayFilter (const Key * key, void * argument)
     203             : {
     204       16349 :         const Key * arrayParent = (const Key *) argument;
     205       16349 :         return keyIsDirectBelow (arrayParent, key) && elektraArrayValidateName (key) > 0;
     206             : }
     207             : 
     208             : 
     209             : /**
     210             :  * @brief Return all the array keys below the given array parent
     211             :  *
     212             :  * The array parent itself is not returned.
     213             :  * For example, if `user/config/#` is an array,
     214             :  * `user/config` is the array parent.
     215             :  * Only the direct array keys will be returned. This means
     216             :  * that for example `user/config/#1/key` will not be included,
     217             :  * but only `user/config/#1`.
     218             :  *
     219             :  * A new keyset will be allocated for the resulting keys.
     220             :  * This means that the caller must `ksDel` the resulting keyset.
     221             :  *
     222             :  * @param arrayParent the parent of the array to be returned
     223             :  * @param keys the keyset containing the array keys
     224             :  *
     225             :  * @return a keyset containing the array keys (if any)
     226             :  * @retval NULL on `NULL` pointers
     227             :  */
     228         757 : KeySet * elektraArrayGet (const Key * arrayParent, KeySet * keys)
     229             : {
     230         757 :         if (!arrayParent) return 0;
     231             : 
     232         757 :         if (!keys) return 0;
     233             : 
     234         730 :         KeySet * arrayKeys = ksNew (ksGetSize (keys), KS_END);
     235         730 :         elektraKsFilter (arrayKeys, keys, &arrayFilter, (void *) arrayParent);
     236         730 :         return arrayKeys;
     237             : }
     238             : 
     239             : /**
     240             :  *
     241             :  * Return the next key in the given array.
     242             :  * The function will automatically allocate memory
     243             :  * for a new key and name it accordingly.
     244             :  *
     245             :  * @pre The supplied keyset must contain only valid array keys.
     246             :  *
     247             :  * The caller has to keyDel the resulting key.
     248             :  *
     249             :  * @param arrayKeys the array where the new key will belong to
     250             :  *
     251             :  * @return the new array key on success
     252             :  * @retval NULL if the passed array is empty
     253             :  * @retval NULL on NULL pointers or if an error occurs
     254             :  */
     255         117 : Key * elektraArrayGetNextKey (KeySet * arrayKeys)
     256             : {
     257         117 :         if (!arrayKeys) return 0;
     258             : 
     259         117 :         Key * last = ksPop (arrayKeys);
     260             : 
     261         117 :         if (!last) return 0;
     262             : 
     263         113 :         ksAppendKey (arrayKeys, last);
     264         113 :         Key * newKey = keyDup (last);
     265         113 :         keySetBinary (newKey, 0, 0);
     266         113 :         int ret = elektraArrayIncName (newKey);
     267             : 
     268         113 :         if (ret == -1)
     269             :         {
     270           0 :                 keyDel (newKey);
     271           0 :                 return 0;
     272             :         }
     273             : 
     274             :         return newKey;
     275             : }

Generated by: LCOV version 1.13