LCOV - code coverage report
Current view: top level - src/plugins/rename - rename.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 194 201 96.5 %
Date: 2019-09-12 12:28:41 Functions: 8 9 88.9 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Implementation of plugin rename
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  *
       8             :  */
       9             : 
      10             : #include "rename.h"
      11             : 
      12             : #ifndef HAVE_KDBCONFIG
      13             : #include "kdbconfig.h"
      14             : #endif
      15             : 
      16             : 
      17             : #include <ctype.h>
      18             : #include <errno.h>
      19             : #include <kdbhelper.h>
      20             : #include <stdbool.h>
      21             : #include <stdio.h>
      22             : #include <stdlib.h>
      23             : 
      24             : #include <kdbobsolete.h> // for keyNameGetOneLevel
      25             : #include <kdbprivate.h>  // for access to sync bit (keyClearSync)
      26             : 
      27             : #define ELEKTRA_ORIGINAL_NAME_META "origname"
      28             : #define TOLOWER (-1)
      29             : #define TOUPPER 1
      30             : #define UNCHNGD 0
      31             : #define KEYNAME 2
      32             : 
      33          56 : static void doConversion (char * newName, int levels, const int toCase)
      34             : {
      35             :         int (*conversion) (int);
      36             : 
      37          56 :         if (toCase == TOUPPER)
      38             :         {
      39             :                 conversion = toupper;
      40             :         }
      41             :         else
      42             :         {
      43          21 :                 conversion = tolower;
      44             :         }
      45          56 :         char * returnName = elektraCalloc (strlen (newName) + 1);
      46          56 :         if (levels == 0)
      47             :         {
      48             :                 unsigned int i = 0;
      49         624 :                 for (; i < strlen (newName); ++i)
      50             :                 {
      51         624 :                         returnName[i] = conversion (newName[i]);
      52             :                 }
      53             :         }
      54             :         else
      55             :         {
      56          16 :                 short levelCount = 0;
      57          16 :                 int i = 0;
      58         264 :                 for (i = strlen (newName); i >= 0 && levelCount < levels; --i)
      59             :                 {
      60         248 :                         if (newName[i] == '/')
      61             :                         {
      62          30 :                                 ++levelCount;
      63          30 :                                 returnName[i] = newName[i];
      64          30 :                                 continue;
      65             :                         }
      66         218 :                         returnName[i] = conversion (newName[i]);
      67             :                 }
      68         268 :                 for (; i >= 0; --i)
      69             :                 {
      70         268 :                         returnName[i] = newName[i];
      71             :                 }
      72             :         }
      73          56 :         strncpy (newName, returnName, strlen (newName));
      74          56 :         free (returnName);
      75          56 : }
      76             : 
      77         117 : Key * elektraKeyCreateNewName (const Key * key, const Key * parentKey, const char * cutPath, const char * replaceWith,
      78             :                                const char * toUpperPath, const char * toLowerPath, const int initialConversion)
      79             : {
      80         117 :         size_t addToLen = 0;
      81         117 :         if (replaceWith != NULL) addToLen = strlen (replaceWith);
      82             : 
      83         117 :         size_t maxNewLength = strlen (keyName (key)) + addToLen;
      84             : 
      85         117 :         char * newName = elektraCalloc (maxNewLength + 1);
      86         117 :         short replace = 0;
      87             : 
      88         117 :         char * parentKeyName = elektraMalloc (keyGetFullNameSize (parentKey));
      89         117 :         keyGetFullName (parentKey, parentKeyName, keyGetFullNameSize (parentKey));
      90         117 :         char * curKeyName = elektraMalloc (keyGetFullNameSize (key));
      91         117 :         keyGetFullName (key, curKeyName, keyGetFullNameSize (key));
      92             : 
      93         117 :         char * afterParentString = curKeyName + (strlen (parentKeyName));
      94             :         char * ptr;
      95             : 
      96         117 :         if (initialConversion != UNCHNGD)
      97             :         {
      98          15 :                 doConversion (afterParentString, 0, initialConversion);
      99          15 :                 replace = 1;
     100             :         }
     101             : 
     102         117 :         if (cutPath && (cutPath[0] != '/') && ((ptr = strstr (afterParentString, cutPath)) != NULL))
     103             :         {
     104          42 :                 strncpy (newName, afterParentString, (ptr - afterParentString));
     105          42 :                 if (replaceWith)
     106             :                 {
     107           6 :                         strncpy (newName + strlen (newName), replaceWith, elektraStrLen (replaceWith));
     108             :                 }
     109          42 :                 strncat (newName, ptr + (strlen (cutPath)), strlen (afterParentString) - strlen (cutPath));
     110          42 :                 replace = 1;
     111             :         }
     112             :         else
     113             :         {
     114          75 :                 strncpy (newName, afterParentString, elektraStrLen (afterParentString));
     115             :         }
     116         139 :         int toLower = toLowerPath ? atoi (toLowerPath) : 0;
     117         145 :         int toUpper = toUpperPath ? atoi (toUpperPath) : 0;
     118             : 
     119         117 :         if (strlen (newName) > 0)
     120             :         {
     121          91 :                 if (toUpperPath && toLowerPath)
     122             :                 {
     123           6 :                         if (toUpper < toLower)
     124             :                         {
     125           2 :                                 doConversion (newName, toLower, TOLOWER);
     126           2 :                                 doConversion (newName, toUpper, TOUPPER);
     127             :                         }
     128             :                         else
     129             :                         {
     130           4 :                                 doConversion (newName, toUpper, TOUPPER);
     131           4 :                                 doConversion (newName, toLower, TOLOWER);
     132             :                         }
     133             :                         replace = 1;
     134             :                 }
     135          85 :                 else if (toUpperPath)
     136             :                 {
     137          16 :                         doConversion (newName, toUpper, TOUPPER);
     138          16 :                         replace = 1;
     139             :                 }
     140          69 :                 else if (toLowerPath)
     141             :                 {
     142           8 :                         doConversion (newName, toLower, TOLOWER);
     143           8 :                         replace = 1;
     144             :                 }
     145             :         }
     146         117 :         elektraFree (parentKeyName);
     147         117 :         elektraFree (curKeyName);
     148         117 :         if (replace)
     149             :         {
     150          73 :                 Key * result = keyDup (key);
     151          73 :                 keySetName (result, keyName (parentKey));
     152          73 :                 keyAddName (result, newName);
     153          73 :                 elektraFree (newName);
     154          73 :                 return result;
     155             :         }
     156          44 :         elektraFree (newName);
     157          44 :         return 0;
     158             : }
     159             : 
     160           6 : static void keyAddUnescapedBasePath (Key * key, const char * path)
     161             : {
     162           6 :         size_t size = 0;
     163           6 :         char * p = keyNameGetOneLevel (path + size, &size);
     164          22 :         while (*p)
     165             :         {
     166          10 :                 char * buffer = elektraMalloc (size + 1);
     167          10 :                 strncpy (buffer, p, size);
     168          10 :                 buffer[size] = 0;
     169          10 :                 keyAddBaseName (key, buffer);
     170          10 :                 elektraFree (buffer);
     171          10 :                 p = keyNameGetOneLevel (p + size, &size);
     172             :         }
     173           6 : }
     174             : 
     175         109 : static Key * renameGet (Key * key, Key * parentKey, Key * cutConfig, Key * replaceWithConfig, Key * toUpperConfig, Key * toLowerConfig,
     176             :                         Key * getCase)
     177             : {
     178         109 :         char * cutPath = 0;
     179         109 :         char * replaceWith = 0;
     180         109 :         char * toUpperPath = 0;
     181         109 :         char * toLowerPath = 0;
     182         109 :         const Key * cutMeta = keyGetMeta (key, "rename/cut");
     183         109 :         const Key * toMeta = keyGetMeta (key, "rename/to");
     184         109 :         const Key * toUpperMeta = keyGetMeta (key, "rename/toupper");
     185         109 :         const Key * toLowerMeta = keyGetMeta (key, "rename/tolower");
     186             : 
     187         109 :         int initialConversion = 0;
     188         109 :         if (getCase)
     189             :         {
     190          15 :                 const char * str = keyString (getCase);
     191          15 :                 if (!strcmp (str, "toupper"))
     192             :                 {
     193             :                         initialConversion = TOUPPER;
     194             :                 }
     195           7 :                 else if (!strcmp (str, "tolower"))
     196             :                 {
     197             :                         initialConversion = TOLOWER;
     198             :                 }
     199             :                 else
     200             :                 {
     201           0 :                         initialConversion = UNCHNGD;
     202             :                 }
     203             :         }
     204             :         /* if the meta config exists, it takes precedence over the global config */
     205         109 :         if (cutMeta)
     206           8 :                 cutPath = (char *) keyString (cutMeta);
     207         101 :         else if (cutConfig)
     208          50 :                 cutPath = (char *) keyString (cutConfig);
     209         109 :         if (toMeta)
     210           0 :                 replaceWith = (char *) keyString (toMeta);
     211         109 :         else if (replaceWithConfig)
     212          10 :                 replaceWith = (char *) keyString (replaceWithConfig);
     213         109 :         if (toUpperMeta)
     214           0 :                 toUpperPath = (char *) keyString (toUpperMeta);
     215         109 :         else if (toUpperConfig)
     216          28 :                 toUpperPath = (char *) keyString (toUpperConfig);
     217         109 :         if (toLowerMeta)
     218           0 :                 toLowerPath = (char *) keyString (toLowerMeta);
     219         109 :         else if (toLowerConfig)
     220          22 :                 toLowerPath = (char *) keyString (toLowerConfig);
     221             : 
     222         109 :         return elektraKeyCreateNewName (key, parentKey, cutPath, replaceWith, toUpperPath, toLowerPath, initialConversion);
     223             : }
     224             : 
     225          30 : static Key * restoreKeyName (Key * key, const Key * parentKey, const Key * configKey)
     226             : {
     227          30 :         const Key * origNameKey = keyGetMeta (key, ELEKTRA_ORIGINAL_NAME_META);
     228          30 :         if (origNameKey)
     229             :         {
     230          24 :                 if (strcmp (keyString (origNameKey), keyName (key)))
     231             :                 {
     232          16 :                         int hasSync = keyNeedSync (key); // test_bit(key->flags, KEY_FLAG_SYNC);
     233          16 :                         Key * result = keyDup (key);
     234          16 :                         keySetName (result, keyString (origNameKey));
     235          16 :                         keySetMeta (result, ELEKTRA_ORIGINAL_NAME_META, 0);
     236             : 
     237          16 :                         if (!hasSync)
     238             :                         {
     239           0 :                                 keyClearSync (result);
     240             :                         }
     241             :                         return result;
     242             :                 }
     243             :         }
     244             :         else
     245             :         {
     246           6 :                 if (configKey)
     247             :                 {
     248           4 :                         int hasSync = keyNeedSync (key); // test_bit(key->flags, KEY_FLAG_SYNC);
     249           4 :                         Key * result = keyDup (key);
     250           4 :                         keySetName (result, keyName (parentKey));
     251           4 :                         keyAddUnescapedBasePath (result, keyString (configKey));
     252             : 
     253           4 :                         if (keyGetNameSize (key) > keyGetNameSize (parentKey))
     254             :                         {
     255             :                                 /* this calculation does not work for the parent key but is also not needed */
     256           2 :                                 const char * relativePath = keyName (key) + keyGetNameSize (parentKey);
     257           2 :                                 keyAddUnescapedBasePath (result, relativePath);
     258             :                         }
     259             : 
     260           4 :                         if (!hasSync)
     261             :                         {
     262           0 :                                 keyClearSync (result);
     263             :                         }
     264             :                         return result;
     265             :                 }
     266             :         }
     267             : 
     268             :         return 0;
     269             : }
     270             : 
     271         119 : int elektraRenameGet (Plugin * handle, KeySet * returned, Key * parentKey)
     272             : {
     273             :         /* configuration only */
     274         119 :         if (!strcmp (keyName (parentKey), "system/elektra/modules/rename"))
     275             :         {
     276          86 :                 KeySet * info =
     277             : #include "contract.h"
     278             : 
     279          86 :                         ksAppend (returned, info);
     280          86 :                 ksDel (info);
     281          86 :                 return 1;
     282             :         }
     283             : 
     284             : 
     285          33 :         KeySet * config = elektraPluginGetConfig (handle);
     286          33 :         KeySet * iterateKs = ksDup (returned);
     287             : 
     288          33 :         ksRewind (iterateKs);
     289             : 
     290          33 :         Key * cutConfig = ksLookupByName (config, "/cut", KDB_O_NONE);
     291          33 :         Key * toUpper = ksLookupByName (config, "/toupper", KDB_O_NONE);
     292          33 :         Key * toLower = ksLookupByName (config, "/tolower", KDB_O_NONE);
     293          33 :         Key * replaceWith = ksLookupByName (config, "/replacewith", KDB_O_NONE);
     294          33 :         Key * getCase = ksLookupByName (config, "/get/case", KDB_O_NONE);
     295             : 
     296             : 
     297             :         Key * key;
     298         175 :         while ((key = ksNext (iterateKs)) != 0)
     299             :         {
     300             : 
     301         109 :                 Key * renamedKey = renameGet (key, parentKey, cutConfig, replaceWith, toUpper, toLower, getCase);
     302             : 
     303         109 :                 if (renamedKey)
     304             :                 {
     305          69 :                         keySetMeta (renamedKey, ELEKTRA_ORIGINAL_NAME_META, keyName (key));
     306          69 :                         ksLookup (returned, key, KDB_O_POP);
     307          69 :                         keyDel (key);
     308             : 
     309             :                         /*
     310             :                          * if the parentKey is replaced by a rename operation
     311             :                          * make sure that we do not loose its reference (ksAppendKey
     312             :                          * would otherwise delete it)
     313             :                          */
     314          69 :                         if (keyCmp (renamedKey, parentKey) == 0)
     315             :                         {
     316             :                                 /* make sure the parent key is not deleted */
     317           8 :                                 keyIncRef (parentKey);
     318           8 :                                 ksAppendKey (returned, renamedKey);
     319           8 :                                 keyDecRef (parentKey);
     320             :                         }
     321             :                         else
     322             :                         {
     323          61 :                                 ksAppendKey (returned, renamedKey);
     324             :                         }
     325             :                 }
     326             :                 else
     327             :                 {
     328          40 :                         keySetMeta (key, ELEKTRA_ORIGINAL_NAME_META, keyName (key));
     329             :                 }
     330             :         }
     331             : 
     332             :         /* make sure the parent key is not deleted */
     333          33 :         keyIncRef (parentKey);
     334          33 :         ksDel (iterateKs);
     335          33 :         keyDecRef (parentKey);
     336             : 
     337          33 :         return 1; /* success */
     338             : }
     339             : 
     340          15 : int elektraRenameSet (Plugin * handle, KeySet * returned, Key * parentKey)
     341             : {
     342             : 
     343          15 :         KeySet * iterateKs = ksDup (returned);
     344             : 
     345          15 :         KeySet * config = elektraPluginGetConfig (handle);
     346          15 :         Key * cutConfig = ksLookupByName (config, "/cut", KDB_O_NONE);
     347             : 
     348          15 :         Key * setCase = ksLookupByName (config, "/set/case", KDB_O_NONE);
     349             : 
     350          15 :         int writeConversion = 0;
     351          15 :         if (setCase)
     352             :         {
     353           6 :                 const char * str = keyString (setCase);
     354           6 :                 if (!strcmp (str, "toupper"))
     355             :                 {
     356             :                         writeConversion = TOUPPER;
     357             :                 }
     358           3 :                 else if (!strcmp (str, "tolower"))
     359             :                 {
     360             :                         writeConversion = TOLOWER;
     361             :                 }
     362           3 :                 else if (!strcmp (str, "keyname"))
     363             :                 {
     364             :                         writeConversion = KEYNAME;
     365             :                 }
     366             :                 else
     367             :                 {
     368           0 :                         writeConversion = UNCHNGD;
     369             :                 }
     370             :         }
     371          15 :         ksRewind (iterateKs);
     372             :         Key * key;
     373          15 :         char * parentKeyName = elektraMalloc (keyGetFullNameSize (parentKey));
     374          15 :         keyGetFullName (parentKey, parentKeyName, keyGetFullNameSize (parentKey));
     375          66 :         while ((key = ksNext (iterateKs)) != 0)
     376             :         {
     377          36 :                 Key * renamedKey = NULL;
     378          36 :                 if (writeConversion != KEYNAME)
     379             :                 {
     380          30 :                         renamedKey = restoreKeyName (key, parentKey, cutConfig);
     381             : 
     382          30 :                         if (!renamedKey) renamedKey = keyDup (key);
     383          30 :                         if (writeConversion == TOUPPER || writeConversion == TOLOWER)
     384             :                         {
     385           5 :                                 char * curKeyName = elektraMalloc (keyGetFullNameSize (renamedKey));
     386           5 :                                 keyGetFullName (renamedKey, curKeyName, keyGetFullNameSize (renamedKey));
     387             : 
     388           5 :                                 char * afterParentString = curKeyName + (strlen (parentKeyName));
     389             : 
     390           5 :                                 doConversion (afterParentString, 0, writeConversion);
     391             : 
     392           5 :                                 keySetName (renamedKey, curKeyName);
     393           5 :                                 elektraFree (curKeyName);
     394             :                         }
     395             :                         /*
     396             :                          * if something is restored from the parentKey, do
     397             :                          * not delete the parentKey (might cause troubles)
     398             :                          */
     399          30 :                         if (keyCmp (key, parentKey) != 0)
     400             :                         {
     401          22 :                                 keyDel (ksLookup (returned, key, KDB_O_POP));
     402             :                         }
     403          30 :                         ksAppendKey (returned, renamedKey);
     404          30 :                         keyDel (renamedKey);
     405             :                 }
     406             :                 else
     407             :                 {
     408           6 :                         if (keyCmp (key, parentKey) != 0)
     409             :                         {
     410           3 :                                 keyDel (ksLookupByName (returned, keyString (keyGetMeta (key, ELEKTRA_ORIGINAL_NAME_META)), KDB_O_POP));
     411             :                         }
     412           6 :                         ksAppendKey (returned, key);
     413             :                 }
     414             :         }
     415             : 
     416          15 :         keyIncRef (parentKey);
     417          15 :         ksDel (iterateKs);
     418          15 :         keyDecRef (parentKey);
     419             : 
     420          15 :         ksRewind (returned);
     421          15 :         elektraFree (parentKeyName);
     422          15 :         return 1; /* success */
     423             : }
     424             : 
     425         176 : Plugin * ELEKTRA_PLUGIN_EXPORT
     426             : {
     427             :         // clang-format off
     428         176 :         return elektraPluginExport("rename",
     429             :                 ELEKTRA_PLUGIN_GET,     &elektraRenameGet,
     430             :                 ELEKTRA_PLUGIN_SET,     &elektraRenameSet,
     431             :                 ELEKTRA_PLUGIN_END);
     432             : }

Generated by: LCOV version 1.13