1 /*
2 * Compiz configuration system library
3 *
4 * Copyright (C) 2007 Danny Baumann <maniac@opencompositing.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #define _GNU_SOURCE
22 #include <stdio.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <errno.h>
26
27 #include <ccs.h>
28 #include "iniparser.h"
29
30 /**
31 * Creates the parent directory for @fileName, recursively creating a directory
32 * tree if necessary.
33 *
34 * @param fileName: The absolute path to the desired file
35 * @return: True if the parent directory of the file now exists
36 **/
37
38 Bool
ccsCreateDirFor(const char * fileName)39 ccsCreateDirFor (const char *fileName)
40 {
41 char *path, *delim;
42 Bool success;
43
44 delim = strrchr (fileName, '/');
45 if (!delim)
46 return FALSE; /* Input string is not a valid absolue path! */
47
48 path = malloc (delim - fileName + 1);
49 if (!path)
50 return FALSE;
51
52 strncpy (path, fileName, delim - fileName);
53 path[delim - fileName] = '\0';
54
55 success = !mkdir (path, 0700); /* Mkdir returns 0 on success */
56 success |= (errno == EEXIST);
57
58 if (!success && (errno == ENOENT)) /* ENOENT means we must recursively */
59 { /* create the parent's parent */
60 if (ccsCreateDirFor (path))
61 success = !mkdir (path, 0700);
62 }
63
64 free (path);
65 return success;
66 }
67
ccsIniOpen(const char * fileName)68 IniDictionary * ccsIniOpen (const char * fileName)
69 {
70 FILE *file;
71
72 if (!ccsCreateDirFor(fileName))
73 return NULL;
74
75 /* create file if it doesn't exist or is desired */
76 file = fopen (fileName, "a+");
77 if (file)
78 fclose (file);
79
80 return iniparser_new ((char*) fileName);
81 }
82
83 IniDictionary*
ccsIniNew(void)84 ccsIniNew (void)
85 {
86 return dictionary_new (0);
87 }
88
89 void
ccsIniClose(IniDictionary * dictionary)90 ccsIniClose (IniDictionary *dictionary)
91 {
92 iniparser_free (dictionary);
93 }
94
95 void
ccsIniSave(IniDictionary * dictionary,const char * fileName)96 ccsIniSave (IniDictionary *dictionary,
97 const char *fileName)
98 {
99 if (!ccsCreateDirFor (fileName))
100 return;
101
102 iniparser_dump_ini (dictionary, fileName);
103 }
104
105 static char*
getIniString(IniDictionary * dictionary,const char * section,const char * entry)106 getIniString (IniDictionary *dictionary,
107 const char *section,
108 const char *entry)
109 {
110 char *sectionName;
111 char *retValue;
112
113 asprintf (§ionName, "%s:%s", section, entry);
114
115 retValue = iniparser_getstring (dictionary, sectionName, NULL);
116 free (sectionName);
117
118 return retValue;
119 }
120
121 static void
setIniString(IniDictionary * dictionary,const char * section,const char * entry,const char * value)122 setIniString (IniDictionary *dictionary,
123 const char *section,
124 const char *entry,
125 const char *value)
126 {
127 char *sectionName;
128
129 asprintf (§ionName, "%s:%s", section, entry);
130
131 if (!iniparser_find_entry (dictionary, (char*) section))
132 iniparser_add_entry (dictionary, (char*) section, NULL, NULL);
133
134 iniparser_setstr (dictionary, sectionName, (char*) value);
135
136 free (sectionName);
137 }
138
139 Bool
ccsIniGetString(IniDictionary * dictionary,const char * section,const char * entry,char ** value)140 ccsIniGetString (IniDictionary *dictionary,
141 const char *section,
142 const char *entry,
143 char **value)
144 {
145 char *retValue;
146
147 retValue = getIniString (dictionary, section, entry);
148 if (retValue)
149 {
150 *value = strdup (retValue);
151 return TRUE;
152 }
153 else
154 return FALSE;
155 }
156
157 Bool
ccsIniGetInt(IniDictionary * dictionary,const char * section,const char * entry,int * value)158 ccsIniGetInt (IniDictionary *dictionary,
159 const char *section,
160 const char *entry,
161 int *value)
162 {
163 char *retValue;
164
165 retValue = getIniString (dictionary, section, entry);
166 if (retValue)
167 {
168 *value = strtoul (retValue, NULL, 10);
169 return TRUE;
170 }
171 else
172 return FALSE;
173 }
174
175 Bool
ccsIniGetFloat(IniDictionary * dictionary,const char * section,const char * entry,float * value)176 ccsIniGetFloat (IniDictionary *dictionary,
177 const char *section,
178 const char *entry,
179 float *value)
180 {
181 char *retValue;
182
183 retValue = getIniString (dictionary, section, entry);
184 if (retValue)
185 {
186 *value = (float) strtod (retValue, NULL);
187 return TRUE;
188 }
189 else
190 return FALSE;
191 }
192
193 Bool
ccsIniGetBool(IniDictionary * dictionary,const char * section,const char * entry,Bool * value)194 ccsIniGetBool (IniDictionary *dictionary,
195 const char *section,
196 const char *entry,
197 Bool *value)
198 {
199 char *retValue;
200
201 retValue = getIniString (dictionary, section, entry);
202 if (retValue)
203 {
204 if ((retValue[0] == 't') || (retValue[0] == 'T') ||
205 (retValue[0] == 'y') || (retValue[0] == 'Y') ||
206 (retValue[0] == '1'))
207 {
208 *value = TRUE;
209 }
210 else
211 *value = FALSE;
212
213 return TRUE;
214 }
215 else
216 return FALSE;
217 }
218
219 Bool
ccsIniGetColor(IniDictionary * dictionary,const char * section,const char * entry,CCSSettingColorValue * value)220 ccsIniGetColor (IniDictionary *dictionary,
221 const char *section,
222 const char *entry,
223 CCSSettingColorValue *value)
224 {
225 char *retValue;
226
227 retValue = getIniString (dictionary, section, entry);
228 if (retValue && ccsStringToColor (retValue, value))
229 return TRUE;
230 else
231 return FALSE;
232 }
233
234 Bool
ccsIniGetKey(IniDictionary * dictionary,const char * section,const char * entry,CCSSettingKeyValue * value)235 ccsIniGetKey (IniDictionary *dictionary,
236 const char *section,
237 const char *entry,
238 CCSSettingKeyValue *value)
239 {
240 char *retValue;
241
242 retValue = getIniString (dictionary, section, entry);
243 if (retValue)
244 return ccsStringToKeyBinding (retValue, value);
245 else
246 return FALSE;
247 }
248
249 Bool
ccsIniGetButton(IniDictionary * dictionary,const char * section,const char * entry,CCSSettingButtonValue * value)250 ccsIniGetButton (IniDictionary *dictionary,
251 const char *section,
252 const char *entry,
253 CCSSettingButtonValue *value)
254 {
255 char *retValue;
256
257 retValue = getIniString (dictionary, section, entry);
258 if (retValue)
259 return ccsStringToButtonBinding (retValue, value);
260 else
261 return FALSE;
262 }
263
264 Bool
ccsIniGetEdge(IniDictionary * dictionary,const char * section,const char * entry,unsigned int * value)265 ccsIniGetEdge (IniDictionary *dictionary,
266 const char *section,
267 const char *entry,
268 unsigned int *value)
269 {
270 char *retValue;
271
272 retValue = getIniString (dictionary, section, entry);
273 if (retValue)
274 {
275 *value = ccsStringToEdges (retValue);
276 return TRUE;
277 }
278 else
279 return FALSE;
280 }
281
282 Bool
ccsIniGetBell(IniDictionary * dictionary,const char * section,const char * entry,Bool * value)283 ccsIniGetBell (IniDictionary *dictionary,
284 const char *section,
285 const char *entry,
286 Bool *value)
287 {
288 return ccsIniGetBool (dictionary, section, entry, value);
289 }
290
291 static Bool
isEmptyString(char * value)292 isEmptyString (char *value)
293 {
294 int len, i = 0;
295
296 len = strlen (value);
297 for (i = 0; i < len; i++)
298 {
299 if (!isblank (value[i]))
300 return FALSE;
301 }
302 return TRUE;
303 }
304
305 Bool
ccsIniGetList(IniDictionary * dictionary,const char * section,const char * entry,CCSSettingValueList * value,CCSSetting * parent)306 ccsIniGetList (IniDictionary *dictionary,
307 const char *section,
308 const char *entry,
309 CCSSettingValueList *value,
310 CCSSetting *parent)
311 {
312 CCSSettingValueList list = NULL;
313 char *valueString, *valueStart, *valString;
314 char *token;
315 int nItems = 1, i = 0, len;
316
317 valString = getIniString (dictionary, section, entry);
318 if (!valString)
319 return FALSE;
320
321 if (isEmptyString (valString))
322 {
323 *value = NULL;
324 return TRUE;
325 }
326
327 valueString = strdup (valString);
328 valueStart = valueString;
329
330 /* remove trailing semicolon that we added to be able to differentiate
331 between an empty list and a list with one empty item */
332 len = strlen (valueString);
333 if (valueString[len - 1] == ';')
334 valueString[len - 1] = 0;
335
336 token = strchr (valueString, ';');
337 while (token)
338 {
339 token = strchr (token + 1, ';');
340 nItems++;
341 }
342
343 token = strsep (&valueString, ";");
344 switch (parent->info.forList.listType)
345 {
346 case TypeString:
347 case TypeMatch:
348 {
349 char **array = malloc (nItems * sizeof (char*));
350 if (!array)
351 break;
352
353 while (token)
354 {
355 array[i++] = strdup (token);
356 token = strsep (&valueString, ";");
357 }
358
359 list = ccsGetValueListFromStringArray (array, nItems, parent);
360
361 for (i = 0; i < nItems; i++)
362 free (array[i]);
363
364 free (array);
365 }
366 break;
367 case TypeColor:
368 {
369 CCSSettingColorValue *array;
370 array = malloc (nItems * sizeof (CCSSettingColorValue));
371 if (!array)
372 break;
373
374 while (token)
375 {
376 memset (&array[i], 0, sizeof (CCSSettingColorValue));
377 ccsStringToColor (token, &array[i]);
378 token = strsep (&valueString, ";");
379 i++;
380 }
381
382 list = ccsGetValueListFromColorArray (array, nItems, parent);
383 free (array);
384 }
385 break;
386 case TypeBool:
387 {
388 Bool *array = malloc (nItems * sizeof (Bool));
389 Bool isTrue;
390 if (!array)
391 break;
392
393 while (token)
394 {
395 isTrue = (token[0] == 'y' || token[0] == 'Y' ||
396 token[0] == '1' ||
397 token[0] == 't' || token[0] == 'T');
398 array[i++] = isTrue;
399 token = strsep (&valueString, ";");
400 }
401
402 list = ccsGetValueListFromBoolArray (array, nItems, parent);
403 free (array);
404 }
405 break;
406 case TypeInt:
407 {
408 int *array = malloc (nItems * sizeof (int));
409 if (!array)
410 break;
411
412 while (token)
413 {
414 array[i++] = strtoul (token, NULL, 10);
415 token = strsep (&valueString, ";");
416 }
417
418 list = ccsGetValueListFromIntArray (array, nItems, parent);
419 free (array);
420 }
421 break;
422 case TypeFloat:
423 {
424 float *array = malloc (nItems * sizeof (float));
425 if (!array)
426 break;
427
428 while (token)
429 {
430 array[i++] = strtod (token, NULL);
431 token = strsep (&valueString, ";");
432 }
433
434 list = ccsGetValueListFromFloatArray (array, nItems, parent);
435 free (array);
436 }
437 break;
438 case TypeKey:
439 {
440 CCSSettingValue *val = NULL;
441 list = NULL;
442
443 while (token)
444 {
445 val = malloc (sizeof (CCSSettingValue));
446 if (!val)
447 break;
448 if (ccsStringToKeyBinding (token, &val->value.asKey))
449 list = ccsSettingValueListAppend (list, val);
450 else
451 free (val);
452 token = strsep (&valueString, ";");
453 }
454 }
455 break;
456 case TypeButton:
457 {
458 CCSSettingValue *val = NULL;
459 list = NULL;
460
461 while (token)
462 {
463 val = malloc (sizeof (CCSSettingValue));
464 if (!val)
465 break;
466 if (ccsStringToButtonBinding (token, &val->value.asButton))
467 list = ccsSettingValueListAppend (list, val);
468 else
469 free (val);
470 token = strsep (&valueString, ";");
471 }
472 }
473 break;
474 case TypeEdge:
475 {
476 CCSSettingValue *val = NULL;
477 list = NULL;
478
479 while (token)
480 {
481 val = malloc (sizeof (CCSSettingValue));
482 if (!val)
483 break;
484 val->value.asEdge = ccsStringToEdges (token);
485 list = ccsSettingValueListAppend (list, val);
486 token = strsep (&valueString, ";");
487 }
488 }
489 break;
490 case TypeBell:
491 {
492 CCSSettingValue *val = NULL;
493 list = NULL;
494 Bool isTrue;
495
496 while (token)
497 {
498 val = malloc (sizeof (CCSSettingValue));
499 if (!val)
500 break;
501
502 isTrue = (token[0] == 'y' || token[0] == 'Y' ||
503 token[0] == '1' ||
504 token[0] == 't' || token[0] == 'T');
505
506 val->value.asBell = isTrue;
507 list = ccsSettingValueListAppend (list, val);
508 token = strsep (&valueString, ";");
509 }
510 }
511 break;
512 default:
513 break;
514 }
515
516 *value = list;
517 free (valueStart);
518
519 return TRUE;
520 }
521
522 void
ccsIniSetString(IniDictionary * dictionary,const char * section,const char * entry,char * value)523 ccsIniSetString (IniDictionary * dictionary,
524 const char * section,
525 const char * entry,
526 char * value)
527 {
528 setIniString (dictionary, section, entry, value);
529 }
530
531 void
ccsIniSetInt(IniDictionary * dictionary,const char * section,const char * entry,int value)532 ccsIniSetInt (IniDictionary *dictionary,
533 const char *section,
534 const char *entry,
535 int value)
536 {
537 char *string = NULL;
538
539 asprintf (&string, "%i", value);
540 if (string)
541 {
542 setIniString (dictionary, section, entry, string);
543 free (string);
544 }
545 }
546
547 void
ccsIniSetFloat(IniDictionary * dictionary,const char * section,const char * entry,float value)548 ccsIniSetFloat (IniDictionary *dictionary,
549 const char *section,
550 const char *entry,
551 float value)
552 {
553 char *string = NULL;
554
555 asprintf (&string, "%f", value);
556 if (string)
557 {
558 setIniString (dictionary, section, entry, string);
559 free (string);
560 }
561 }
562
563 void
ccsIniSetBool(IniDictionary * dictionary,const char * section,const char * entry,Bool value)564 ccsIniSetBool (IniDictionary *dictionary,
565 const char *section,
566 const char *entry,
567 Bool value)
568 {
569 setIniString (dictionary, section, entry,
570 value ? "true" : "false");
571 }
572
573 void
ccsIniSetColor(IniDictionary * dictionary,const char * section,const char * entry,CCSSettingColorValue value)574 ccsIniSetColor (IniDictionary *dictionary,
575 const char *section,
576 const char *entry,
577 CCSSettingColorValue value)
578 {
579 char *string;
580
581 string = ccsColorToString (&value);
582 if (string)
583 {
584 setIniString (dictionary, section, entry, string);
585 free (string);
586 }
587 }
588
589 void
ccsIniSetKey(IniDictionary * dictionary,const char * section,const char * entry,CCSSettingKeyValue value)590 ccsIniSetKey (IniDictionary *dictionary,
591 const char *section,
592 const char *entry,
593 CCSSettingKeyValue value)
594 {
595 char *str;
596
597 str = ccsKeyBindingToString (&value);
598 if (str)
599 {
600 setIniString (dictionary, section, entry, str);
601 free (str);
602 }
603 }
604
605 void
ccsIniSetButton(IniDictionary * dictionary,const char * section,const char * entry,CCSSettingButtonValue value)606 ccsIniSetButton (IniDictionary *dictionary,
607 const char *section,
608 const char *entry,
609 CCSSettingButtonValue value)
610 {
611 char *str;
612
613 str = ccsButtonBindingToString (&value);
614 if (str)
615 {
616 setIniString (dictionary, section, entry, str);
617 free (str);
618 }
619 }
620
621 void
ccsIniSetEdge(IniDictionary * dictionary,const char * section,const char * entry,unsigned int value)622 ccsIniSetEdge (IniDictionary *dictionary,
623 const char *section,
624 const char *entry,
625 unsigned int value)
626 {
627 char *str;
628
629 str = ccsEdgesToString (value);
630 if (str)
631 {
632 setIniString (dictionary, section, entry, str);
633 free (str);
634 }
635 }
636
637 void
ccsIniSetBell(IniDictionary * dictionary,const char * section,const char * entry,Bool value)638 ccsIniSetBell (IniDictionary *dictionary,
639 const char *section,
640 const char *entry,
641 Bool value)
642 {
643 ccsIniSetBool (dictionary, section, entry, value);
644 }
645
646 void
ccsIniSetList(IniDictionary * dictionary,const char * section,const char * entry,CCSSettingValueList value,CCSSettingType listType)647 ccsIniSetList (IniDictionary *dictionary,
648 const char *section,
649 const char *entry,
650 CCSSettingValueList value,
651 CCSSettingType listType)
652 {
653 char *stringBuffer, *valueString;
654 char valueBuffer[100];
655 unsigned int bufferSize = 1024, fill;
656
657 stringBuffer = calloc (1, bufferSize);
658 if (!stringBuffer)
659 return;
660
661 while (value)
662 {
663 switch (listType)
664 {
665 case TypeString:
666 valueString = value->data->value.asString;
667 break;
668 case TypeMatch:
669 valueString = value->data->value.asMatch;
670 break;
671 case TypeInt:
672 snprintf (valueBuffer, 100, "%d", value->data->value.asInt);
673 valueString = valueBuffer;
674 break;
675 case TypeBool:
676 strncpy (valueBuffer,
677 (value->data->value.asBool) ? "true" : "false", 100);
678 valueString = valueBuffer;
679 break;
680 case TypeFloat:
681 snprintf (valueBuffer, 100, "%f", value->data->value.asFloat);
682 valueString = valueBuffer;
683 break;
684 case TypeColor:
685 valueString = ccsColorToString (&value->data->value.asColor);
686 break;
687 case TypeKey:
688 valueString = ccsKeyBindingToString (&value->data->value.asKey);
689 break;
690 case TypeButton:
691 valueString =
692 ccsButtonBindingToString (&value->data->value.asButton);
693 break;
694 case TypeEdge:
695 valueString = ccsEdgesToString (value->data->value.asEdge);
696 break;
697 case TypeBell:
698 strncpy (valueBuffer,
699 (value->data->value.asBell) ? "true" : "false", 100);
700 valueString = valueBuffer;
701 break;
702 default:
703 valueString = NULL;
704 break;
705 }
706
707 if (!valueString)
708 return;
709
710 fill = strlen (stringBuffer);
711 /* the + 1 is the semicolon we're going to add */
712 if ((fill + strlen (valueString) + 1) >= bufferSize)
713 {
714 /* buffer is too small, make it larger */
715 bufferSize *= 2;
716 stringBuffer = realloc (stringBuffer, bufferSize);
717 if (!stringBuffer)
718 return;
719
720 /* properly NULL terminate it */
721 stringBuffer[fill] = 0;
722 }
723
724 /* we made sure that the buffer is large enough before, so
725 there is no need for strncat */
726 strcat (stringBuffer, valueString);
727 strcat (stringBuffer, ";");
728
729 if (listType == TypeColor || listType == TypeKey ||
730 listType == TypeButton || listType == TypeEdge)
731 {
732 free (valueString);
733 }
734
735 value = value->next;
736 }
737
738 setIniString (dictionary, section, entry, stringBuffer);
739 free (stringBuffer);
740 }
741
ccsIniRemoveEntry(IniDictionary * dictionary,const char * section,const char * entry)742 void ccsIniRemoveEntry (IniDictionary * dictionary,
743
744 const char * section,
745 const char * entry)
746 {
747 char *sectionName;
748
749 asprintf (§ionName, "%s:%s", section, entry);
750 iniparser_unset (dictionary, sectionName);
751 free (sectionName);
752 }
753