Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Source for rgbcolor plugin
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 :
10 : #include "rgbcolor.h"
11 : #include <kdberrors.h>
12 : #include <kdbhelper.h>
13 : #include <kdbtypes.h>
14 : #include <regex.h>
15 : #include <stdio.h>
16 : #include <stdlib.h>
17 :
18 : typedef struct
19 : {
20 : const char * name;
21 : const kdb_unsigned_long_t value;
22 : } NamedColor;
23 :
24 : static const NamedColor NamedColors[] = { { "aliceblue", 0xf0f8ffff },
25 : { "antiquewhite", 0xfaebd7ff },
26 : { "aqua", 0x00ffffff },
27 : { "aquamarine", 0x7fffd4ff },
28 : { "azure", 0xf0ffffff },
29 : { "beige", 0xf5f5dcff },
30 : { "bisque", 0xffe4c4ff },
31 : { "black", 0x000000ff },
32 : { "blanchedalmond", 0xffebcdff },
33 : { "blue", 0x0000ffff },
34 : { "blueviolet", 0x8a2be2ff },
35 : { "brown", 0xa52a2aff },
36 : { "burlywood", 0xdeb887ff },
37 : { "cadetblue", 0x5f9ea0ff },
38 : { "chartreuse", 0x7fff00ff },
39 : { "chocolate", 0xd2691eff },
40 : { "coral", 0xff7f50ff },
41 : { "cornflowerblue", 0x6495edff },
42 : { "cornsilk", 0xfff8dcff },
43 : { "crimson", 0xdc143cff },
44 : { "cyan", 0x00ffffff },
45 : { "darkblue", 0x00008bff },
46 : { "darkcyan", 0x008b8bff },
47 : { "darkgoldenrod", 0xb8860bff },
48 : { "darkgray", 0xa9a9a9ff },
49 : { "darkgreen", 0x006400ff },
50 : { "darkgrey", 0xa9a9a9ff },
51 : { "darkkhaki", 0xbdb76bff },
52 : { "darkmagenta", 0x8b008bff },
53 : { "darkolivegreen", 0x556b2fff },
54 : { "darkorange", 0xff8c00ff },
55 : { "darkorchid", 0x9932ccff },
56 : { "darkred", 0x8b0000ff },
57 : { "darksalmon", 0xe9967aff },
58 : { "darkseagreen", 0x8fbc8fff },
59 : { "darkslateblue", 0x483d8bff },
60 : { "darkslategray", 0x2f4f4fff },
61 : { "darkslategrey", 0x2f4f4fff },
62 : { "darkturquoise", 0x00ced1ff },
63 : { "darkviolet", 0x9400d3ff },
64 : { "deeppink", 0xff1493ff },
65 : { "deepskyblue", 0x00bfffff },
66 : { "dimgray", 0x696969ff },
67 : { "dimgrey", 0x696969ff },
68 : { "dodgerblue", 0x1e90ffff },
69 : { "firebrick", 0xb22222ff },
70 : { "floralwhite", 0xfffaf0ff },
71 : { "forestgreen", 0x228b22ff },
72 : { "fuchsia", 0xff00ffff },
73 : { "gainsboro", 0xdcdcdcff },
74 : { "ghostwhite", 0xf8f8ffff },
75 : { "gold", 0xffd700ff },
76 : { "goldenrod", 0xdaa520ff },
77 : { "gray", 0x808080ff },
78 : { "green", 0x008000ff },
79 : { "greenyellow", 0xadff2fff },
80 : { "grey", 0x808080ff },
81 : { "honeydew", 0xf0fff0ff },
82 : { "hotpink", 0xff69b4ff },
83 : { "indianred", 0xcd5c5cff },
84 : { "indigo", 0x4b0082ff },
85 : { "ivory", 0xfffff0ff },
86 : { "khaki", 0xf0e68cff },
87 : { "lavender", 0xe6e6faff },
88 : { "lavenderblush", 0xfff0f5ff },
89 : { "lawngreen", 0x7cfc00ff },
90 : { "lemonchiffon", 0xfffacdff },
91 : { "lightblue", 0xadd8e6ff },
92 : { "lightcoral", 0xf08080ff },
93 : { "lightcyan", 0xe0ffffff },
94 : { "lightgoldenrodyellow", 0xfafad2ff },
95 : { "lightgray", 0xd3d3d3ff },
96 : { "lightgreen", 0x90ee90ff },
97 : { "lightgrey", 0xd3d3d3ff },
98 : { "lightpink", 0xffb6c1ff },
99 : { "lightsalmon", 0xffa07aff },
100 : { "lightseagreen", 0x20b2aaff },
101 : { "lightskyblue", 0x87cefaff },
102 : { "lightslategray", 0x778899ff },
103 : { "lightslategrey", 0x778899ff },
104 : { "lightsteelblue", 0xb0c4deff },
105 : { "lightyellow", 0xffffe0ff },
106 : { "lime", 0x00ff00ff },
107 : { "limegreen", 0x32cd32ff },
108 : { "linen", 0xfaf0e6ff },
109 : { "magenta", 0xff00ffff },
110 : { "maroon", 0x800000ff },
111 : { "mediumaquamarine", 0x66cdaaff },
112 : { "mediumblue", 0x0000cdff },
113 : { "mediumorchid", 0xba55d3ff },
114 : { "mediumpurple", 0x9370dbff },
115 : { "mediumseagreen", 0x3cb371ff },
116 : { "mediumslateblue", 0x7b68eeff },
117 : { "mediumspringgreen", 0x00fa9aff },
118 : { "mediumturquoise", 0x48d1ccff },
119 : { "mediumvioletred", 0xc71585ff },
120 : { "midnightblue", 0x191970ff },
121 : { "mintcream", 0xf5fffaff },
122 : { "mistyrose", 0xffe4e1ff },
123 : { "moccasin", 0xffe4b5ff },
124 : { "navajowhite", 0xffdeadff },
125 : { "navy", 0x000080ff },
126 : { "oldlace", 0xfdf5e6ff },
127 : { "olive", 0x808000ff },
128 : { "olivedrab", 0x6b8e23ff },
129 : { "orange", 0xffa500ff },
130 : { "orangered", 0xff4500ff },
131 : { "orchid", 0xda70d6ff },
132 : { "palegoldenrod", 0xeee8aaff },
133 : { "palegreen", 0x98fb98ff },
134 : { "paleturquoise", 0xafeeeeff },
135 : { "palevioletred", 0xdb7093ff },
136 : { "papayawhip", 0xffefd5ff },
137 : { "peachpuff", 0xffdab9ff },
138 : { "peru", 0xcd853fff },
139 : { "pink", 0xffc0cbff },
140 : { "plum", 0xdda0ddff },
141 : { "powderblue", 0xb0e0e6ff },
142 : { "purple", 0x800080ff },
143 : { "red", 0xff0000ff },
144 : { "rosybrown", 0xbc8f8fff },
145 : { "royalblue", 0x4169e1ff },
146 : { "saddlebrown", 0x8b4513ff },
147 : { "salmon", 0xfa8072ff },
148 : { "sandybrown", 0xf4a460ff },
149 : { "seagreen", 0x2e8b57ff },
150 : { "seashell", 0xfff5eeff },
151 : { "sienna", 0xa0522dff },
152 : { "silver", 0xc0c0c0ff },
153 : { "skyblue", 0x87ceebff },
154 : { "slateblue", 0x6a5acdff },
155 : { "slategray", 0x708090ff },
156 : { "slategrey", 0x708090ff },
157 : { "snow", 0xfffafaff },
158 : { "springgreen", 0x00ff7fff },
159 : { "steelblue", 0x4682b4ff },
160 : { "tan", 0xd2b48cff },
161 : { "teal", 0x008080ff },
162 : { "thistle", 0xd8bfd8ff },
163 : { "tomato", 0xff6347ff },
164 : { "turquoise", 0x40e0d0ff },
165 : { "violet", 0xee82eeff },
166 : { "wheat", 0xf5deb3ff },
167 : { "white", 0xffffffff },
168 : { "whitesmoke", 0xf5f5f5ff },
169 : { "yellow", 0xffff00ff },
170 : { "yellowgreen", 0x9acd32ff } };
171 :
172 : typedef enum
173 : {
174 : COLOR_INVALID,
175 : HEX_THREE,
176 : HEX_FOUR,
177 : HEX_SIX,
178 : HEX_EIGHT,
179 : NAMED_COLOR
180 : } ColorVariant;
181 :
182 : /**
183 : * Internal function used for bsearch
184 : */
185 1024 : static int compareNamedColors (const void * c1, const void * c2)
186 : {
187 1024 : NamedColor * nc1 = (NamedColor *) c1;
188 1024 : NamedColor * nc2 = (NamedColor *) c2;
189 1024 : return strcmp (nc1->name, nc2->name);
190 : }
191 :
192 : /**
193 : * Finds a named color with the given name
194 : *
195 : * @param colorName the color name to search for
196 : * @retval NULL if no color with the given name exists
197 : * @retval The NamedColor with the given name
198 : */
199 139 : static const NamedColor * findNamedColor (const char * colorName)
200 : {
201 139 : const NamedColor key = { .name = colorName };
202 : void * found;
203 139 : found = bsearch (&key, &NamedColors[0], 147, sizeof (NamedColor), compareNamedColors);
204 139 : if (found == NULL) return NULL;
205 58 : return (NamedColor *) found;
206 : }
207 :
208 : /**
209 : * Checks wheter the given key contains a valid hex-formatted string or a named color
210 : *
211 : * @param key the key whose value should be checked
212 : * @param parentKey the parent key
213 : */
214 119 : static ColorVariant is_valid_key (Key * key, Key * parentKey)
215 : {
216 119 : const Key * meta = keyGetMeta (key, "check/rgbcolor");
217 119 : if (!meta) return 1;
218 119 : const char * value = keyString (key);
219 :
220 119 : const NamedColor * namedColor = findNamedColor (value);
221 :
222 119 : if (namedColor != NULL)
223 : {
224 : return NAMED_COLOR;
225 : }
226 :
227 81 : if (*value == '#')
228 : {
229 72 : size_t len = strlen (value + 1);
230 72 : size_t lengthValidChars = strspn (value + 1, "0123456789abcdefABCDEF");
231 72 : if (len == lengthValidChars)
232 : {
233 70 : switch (len)
234 : {
235 : case 3:
236 : return HEX_THREE;
237 : case 4:
238 13 : return HEX_FOUR;
239 : case 6:
240 23 : return HEX_SIX;
241 : case 8:
242 14 : return HEX_EIGHT;
243 : }
244 : }
245 : }
246 :
247 14 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey,
248 : "Key '%s' with value '%s' is neither a valid hex formatted color nor a named color",
249 : keyName (key), keyString (key));
250 14 : return COLOR_INVALID;
251 : }
252 :
253 : /**
254 : * Sets the keys value to the given integer formatted as a string
255 : *
256 : * @param key the key to set the value for
257 : * @param c the integer to set as the new value
258 : */
259 49 : static void elektraColorSetInteger (Key * key, kdb_unsigned_long_t c)
260 : {
261 : char colorStr[11];
262 49 : snprintf (colorStr, 11, "%u", c);
263 :
264 : ELEKTRA_LOG_DEBUG ("Set %s to integer %s", keyName (key), colorStr);
265 49 : keySetString (key, colorStr);
266 49 : }
267 :
268 : /**
269 : * Expands all color variants to the full RRGGBBAA variant.
270 : *
271 : * @param str the string to expand.
272 : * @param colVar the ColorVariant of str
273 : * @param expandedStr pre-allocated memory of length 10 to store the expanded string.
274 : */
275 25 : static void elektraColorExpand (const char * str, ColorVariant colVar, char * expandedStr)
276 : {
277 25 : if (colVar == HEX_THREE || colVar == HEX_FOUR)
278 : {
279 12 : expandedStr[0] = '#';
280 :
281 54 : for (size_t i = 1; i < strlen (str); i++)
282 : {
283 42 : expandedStr[2 * i - 1] = str[i];
284 42 : expandedStr[2 * i] = str[i];
285 : }
286 : }
287 : else
288 : {
289 13 : strcpy (expandedStr, str);
290 : }
291 :
292 25 : if (colVar == HEX_THREE || colVar == HEX_SIX)
293 : {
294 19 : expandedStr[7] = 'f';
295 19 : expandedStr[8] = 'f';
296 : }
297 :
298 25 : expandedStr[9] = '\0';
299 25 : }
300 :
301 : /**
302 : * Normalizes a rgb, rgba, rrggbb hexstring to rrggbbaa
303 : *
304 : * @param key the key to normalize
305 : * @param colVar the ColorVariant of the string in key
306 : */
307 49 : static void elektraColorNormalizeHexString (Key * key, ColorVariant colVar)
308 : {
309 49 : const char * str = keyString (key);
310 49 : char * origvalue = elektraStrDup (str);
311 :
312 : kdb_unsigned_long_t color;
313 49 : if (colVar == NAMED_COLOR)
314 : {
315 20 : const NamedColor * namedColor = findNamedColor (str);
316 20 : color = namedColor->value;
317 : }
318 29 : else if (colVar != HEX_EIGHT)
319 : {
320 : char expandedStr[10];
321 25 : elektraColorExpand (str, colVar, expandedStr);
322 : ELEKTRA_LOG_DEBUG ("Expanded %s to %s", str, expandedStr);
323 25 : color = ELEKTRA_UNSIGNED_LONG_LONG_S (expandedStr + 1, NULL, 16);
324 : }
325 : else
326 : {
327 4 : color = ELEKTRA_UNSIGNED_LONG_LONG_S (str + 1, NULL, 16);
328 : }
329 49 : elektraColorSetInteger (key, color);
330 :
331 : ELEKTRA_LOG_DEBUG ("Setting origvalue of %s to %s", keyName (key), origvalue);
332 49 : keySetMeta (key, "origvalue", origvalue);
333 49 : elektraFree (origvalue);
334 49 : }
335 :
336 : /**
337 : * Restores the original value
338 : *
339 : * @param key the key to restore the value for
340 : */
341 70 : static void elektraColorRestore (Key * key)
342 : {
343 70 : const Key * orig = keyGetMeta (key, "origvalue");
344 70 : if (orig != NULL)
345 : {
346 : ELEKTRA_LOG_DEBUG ("Restore %s to %s", keyName (key), keyString (orig));
347 5 : keySetString (key, keyString (orig));
348 : }
349 70 : }
350 :
351 70 : int elektraRgbcolorGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned, Key * parentKey)
352 : {
353 70 : if (!elektraStrCmp (keyName (parentKey), "system/elektra/modules/rgbcolor"))
354 : {
355 31 : KeySet * contract =
356 31 : ksNew (30, keyNew ("system/elektra/modules/rgbcolor", KEY_VALUE, "rgbcolor plugin waits for your orders", KEY_END),
357 : keyNew ("system/elektra/modules/rgbcolor/exports", KEY_END),
358 : keyNew ("system/elektra/modules/rgbcolor/exports/get", KEY_FUNC, elektraRgbcolorGet, KEY_END),
359 : keyNew ("system/elektra/modules/rgbcolor/exports/set", KEY_FUNC, elektraRgbcolorSet, KEY_END),
360 : #include ELEKTRA_README
361 : keyNew ("system/elektra/modules/rgbcolor/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
362 31 : ksAppend (returned, contract);
363 31 : ksDel (contract);
364 :
365 31 : return ELEKTRA_PLUGIN_STATUS_SUCCESS;
366 : }
367 :
368 : Key * cur;
369 39 : ksRewind (returned);
370 129 : while ((cur = ksNext (returned)) != NULL)
371 : {
372 51 : const Key * meta = keyGetMeta (cur, "check/rgbcolor");
373 51 : if (!meta) continue;
374 49 : ColorVariant colVar = is_valid_key (cur, parentKey);
375 49 : elektraColorNormalizeHexString (cur, colVar);
376 : }
377 :
378 : return ELEKTRA_PLUGIN_STATUS_SUCCESS;
379 : }
380 :
381 67 : int elektraRgbcolorSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
382 : {
383 : // set all keys
384 : // this function is optional
385 : Key * cur;
386 67 : ksRewind (returned);
387 192 : while ((cur = ksNext (returned)) != NULL)
388 : {
389 72 : const Key * meta = keyGetMeta (cur, "check/rgbcolor");
390 72 : if (!meta) continue;
391 :
392 70 : elektraColorRestore (cur);
393 :
394 70 : ColorVariant colVar = is_valid_key (cur, parentKey);
395 70 : if (colVar == COLOR_INVALID) return ELEKTRA_PLUGIN_STATUS_ERROR;
396 : }
397 : return ELEKTRA_PLUGIN_STATUS_SUCCESS;
398 : }
399 :
400 163 : Plugin * ELEKTRA_PLUGIN_EXPORT
401 : {
402 : // clang-format off
403 163 : return elektraPluginExport ("rgbcolor",
404 : ELEKTRA_PLUGIN_GET, &elektraRgbcolorGet,
405 : ELEKTRA_PLUGIN_SET, &elektraRgbcolorSet,
406 : ELEKTRA_PLUGIN_END);
407 : }
|