Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief A plugin that converts keys to metakeys and vice versa
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 :
10 : #include "glob.h"
11 :
12 : #ifndef HAVE_KDBCONFIG
13 : #include "kdbconfig.h"
14 : #endif
15 :
16 : #include <fnmatch.h>
17 : #include <kdbhelper.h>
18 :
19 : struct GlobFlagMap
20 : {
21 : char * name;
22 : short flag;
23 : };
24 :
25 : struct GlobFlagMap flagMaps[] = { { "noescape", FNM_NOESCAPE }, { "pathname", FNM_PATHNAME }, { "period", FNM_PERIOD } };
26 :
27 62 : int elektraGlobMatch (Key * key, const Key * match, const char * globFlags)
28 : {
29 62 : char * tokenList = elektraStrDup (globFlags);
30 62 : char delimiter[] = ",";
31 62 : char * flagName = strtok (tokenList, delimiter);
32 :
33 62 : int flags = 0;
34 168 : while (flagName != NULL)
35 : {
36 132 : for (size_t i = 0; i < sizeof (flagMaps) / sizeof (struct GlobFlagMap); i++)
37 : {
38 132 : if (!strcmp (flagName, flagMaps[i].name))
39 : {
40 44 : flags |= flagMaps[i].flag;
41 : }
42 : }
43 44 : flagName = strtok (NULL, delimiter);
44 : }
45 :
46 62 : free (tokenList);
47 :
48 62 : if (!fnmatch (keyString (match), keyName (key), flags))
49 : {
50 34 : keyCopyAllMeta (key, match);
51 34 : return 1;
52 : }
53 :
54 : return 0;
55 : }
56 :
57 : enum GlobDirection
58 : {
59 : GET,
60 : SET,
61 : };
62 :
63 45 : static const char * getGlobFlags (KeySet * keys, Key * globKey)
64 : {
65 45 : Key * flagKey = keyDup (globKey);
66 45 : keyAddBaseName (flagKey, "flags");
67 45 : Key * flagResult = ksLookup (keys, flagKey, KDB_O_NONE);
68 45 : keyDel (flagKey);
69 :
70 45 : if (flagResult)
71 : {
72 10 : return keyString (flagResult);
73 : }
74 :
75 : return 0;
76 : }
77 :
78 33 : static KeySet * getGlobKeys (Key * parentKey, KeySet * keys, enum GlobDirection direction)
79 : {
80 33 : KeySet * glob = ksNew (0, KS_END);
81 33 : Key * k = 0;
82 33 : size_t parentsize = keyGetNameSize (parentKey);
83 :
84 33 : Key * userGlobConfig = 0;
85 33 : Key * systemGlobConfig = 0;
86 33 : Key * userDirGlobConfig = 0;
87 33 : Key * systemDirGlobConfig = 0;
88 :
89 33 : userGlobConfig = keyNew ("user/glob", KEY_END);
90 33 : systemGlobConfig = keyNew ("system/glob", KEY_END);
91 33 : switch (direction)
92 : {
93 : case GET:
94 23 : userDirGlobConfig = keyNew ("user/glob/get", KEY_END);
95 23 : systemDirGlobConfig = keyNew ("system/glob/get", KEY_END);
96 23 : break;
97 : case SET:
98 10 : userDirGlobConfig = keyNew ("user/glob/set", KEY_END);
99 10 : systemDirGlobConfig = keyNew ("system/glob/set", KEY_END);
100 10 : break;
101 : }
102 :
103 203 : while ((k = ksNext (keys)) != 0)
104 : {
105 : /* use only glob keys for the current direction */
106 309 : if (keyIsDirectBelow (userGlobConfig, k) || keyIsDirectBelow (systemGlobConfig, k) ||
107 274 : keyIsDirectBelow (userDirGlobConfig, k) || keyIsDirectBelow (systemDirGlobConfig, k))
108 : {
109 45 : keySetMeta (k, "glob/flags", getGlobFlags (keys, k));
110 :
111 : /* Look if we have a string */
112 45 : size_t valsize = keyGetValueSize (k);
113 45 : if (valsize < 2) continue;
114 :
115 : /* We now know we want that key.
116 : Dup it to not change the configuration. */
117 26 : Key * ins = keyDup (k);
118 : /* Now look if we want cascading for the key */
119 26 : if (keyString (k)[0] == '/')
120 : {
121 18 : char * newstring = elektraMalloc (valsize + parentsize);
122 18 : strcpy (newstring, keyName (parentKey));
123 18 : strcat (newstring, keyString (k));
124 18 : keySetString (ins, newstring);
125 18 : elektraFree (newstring);
126 : }
127 26 : ksAppendKey (glob, ins);
128 : }
129 : }
130 :
131 33 : keyDel (userGlobConfig);
132 33 : keyDel (systemGlobConfig);
133 33 : keyDel (userDirGlobConfig);
134 33 : keyDel (systemDirGlobConfig);
135 :
136 33 : return glob;
137 : }
138 :
139 33 : static void applyGlob (KeySet * returned, KeySet * glob)
140 : {
141 : Key * cur;
142 33 : ksRewind (returned);
143 164 : while ((cur = ksNext (returned)) != 0)
144 : {
145 : Key * match;
146 98 : ksRewind (glob);
147 224 : while ((match = ksNext (glob)) != 0)
148 : {
149 62 : const Key * flagKey = keyGetMeta (match, "glob/flags");
150 : int matchApplied;
151 :
152 62 : if (flagKey)
153 : {
154 24 : matchApplied = elektraGlobMatch (cur, match, keyString (flagKey));
155 : }
156 : else
157 : {
158 : /* if no flags were provided, default to FNM_PATHNAME behaviour */
159 38 : matchApplied = elektraGlobMatch (cur, match, "pathname");
160 : }
161 :
162 62 : if (matchApplied) break;
163 : }
164 : }
165 33 : }
166 :
167 462 : int elektraGlobOpen (Plugin * handle ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
168 : {
169 : /* plugin initialization logic should be here */
170 : /* TODO: name of parentKey is not set...*/
171 : /* So rewriting cannot happen here (is in elektraGlobSet */
172 :
173 462 : return 1; /* success */
174 : }
175 :
176 462 : int elektraGlobClose (Plugin * handle ELEKTRA_UNUSED, Key * errorKey ELEKTRA_UNUSED)
177 : {
178 : /* free all plugin resources and shut it down */
179 :
180 462 : KeySet * keys = elektraPluginGetData (handle);
181 462 : ksDel (keys);
182 :
183 462 : return 1; /* success */
184 : }
185 :
186 :
187 381 : int elektraGlobGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned, Key * parentKey ELEKTRA_UNUSED)
188 : {
189 381 : if (!strcmp (keyName (parentKey), "system/elektra/modules/glob"))
190 : {
191 : // TODO: improve plugin contract
192 358 : KeySet * config =
193 : #include "contract.h"
194 358 : ksAppend (returned, config);
195 358 : ksDel (config);
196 358 : return 1;
197 : }
198 :
199 23 : KeySet * keys = elektraPluginGetConfig (handle);
200 23 : ksRewind (keys);
201 :
202 23 : KeySet * glob = getGlobKeys (parentKey, keys, GET);
203 23 : applyGlob (returned, glob);
204 :
205 23 : ksDel (glob);
206 :
207 23 : return 1; /* success */
208 : }
209 :
210 :
211 10 : int elektraGlobSet (Plugin * handle, KeySet * returned, Key * parentKey)
212 : {
213 10 : KeySet * keys = elektraPluginGetConfig (handle);
214 10 : ksRewind (keys);
215 :
216 10 : KeySet * glob = getGlobKeys (parentKey, keys, SET);
217 10 : applyGlob (returned, glob);
218 :
219 10 : ksDel (glob);
220 :
221 10 : return 1; /* success */
222 : }
223 :
224 462 : Plugin * ELEKTRA_PLUGIN_EXPORT
225 : {
226 : // clang-format off
227 462 : return elektraPluginExport("glob",
228 : ELEKTRA_PLUGIN_OPEN, &elektraGlobOpen,
229 : ELEKTRA_PLUGIN_CLOSE, &elektraGlobClose,
230 : ELEKTRA_PLUGIN_GET, &elektraGlobGet,
231 : ELEKTRA_PLUGIN_SET, &elektraGlobSet,
232 : ELEKTRA_PLUGIN_END);
233 : }
234 :
|