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 : }
|