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 (&sectionName, "%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 (&sectionName, "%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 (&sectionName, "%s:%s", section, entry);
750     iniparser_unset (dictionary, sectionName);
751     free (sectionName);
752 }
753