1 /*
2  * Compiz configuration system library
3  *
4  * Copyright (C) 2007  Dennis Kasprzyk <onestone@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 #ifdef USE_PROTOBUF
22 #include "compizconfig.pb.h"
23 #include <google/protobuf/io/zero_copy_stream_impl.h>
24 #endif
25 
26 extern "C"
27 {
28 #ifdef HAVE_CONFIG_H
29 #  include "../config.h"
30 #endif
31 
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <dirent.h>
36 #include <sys/stat.h>
37 #include <errno.h>
38 
39 #include <libxslt/transform.h>
40 #include <libxslt/xsltutils.h>
41 
42 #include <locale.h>
43 
44 #include <compiz-core.h>
45 #include <ccs.h>
46 #include "ccs-private.h"
47 }
48 
49 extern int xmlLoadExtDtdDefaultValue;
50 
51 static const char *
getLocale()52 getLocale ()
53 {
54     char *lang = getenv ("LC_ALL");
55 
56     if (!lang || !strlen (lang))
57 	lang = getenv ("LC_MESSAGES");
58 
59     if (!lang || !strlen (lang))
60 	lang = getenv ("LANG");
61 
62     return lang ? lang : "";
63 }
64 
65 #ifdef USE_PROTOBUF
66 
67 Bool usingProtobuf = TRUE;
68 
69 #define PB_ABI_VERSION 20090314
70 
71 typedef metadata::PluginInfo PluginInfoMetadata;
72 typedef metadata::PluginBrief PluginBriefMetadata;
73 typedef metadata::Plugin PluginMetadata;
74 
75 typedef PluginInfoMetadata::Dependencies DependenciesMetadata;
76 typedef PluginMetadata::Screen ScreenMetadata;
77 typedef PluginMetadata::Option OptionMetadata;
78 typedef PluginMetadata::Extension ExtensionMetadata;
79 typedef OptionMetadata::GenericValue GenericValueMetadata;
80 
81 typedef google::protobuf::RepeatedPtrField< std::string > StringList;
82 
83 PluginBriefMetadata persistentPluginBriefPB;
84 PluginMetadata persistentPluginPB; // Made global so that it gets reused,
85 					 // for better performance (to avoid
86 					 // mem alloc/free for each plugin)
87 
88 std::string metadataCacheDir = "";
89 
90 std::string curLocale = std::string (getLocale ());
91 std::string shortLocale = curLocale.find ('.') == std::string::npos ?
92     curLocale : curLocale.substr (0, curLocale.find ('.'));
93 
94 #endif
95 
96 static void
ccsAddRestrictionToStringInfo(CCSSettingStringInfo * forString,const char * name,const char * value)97 ccsAddRestrictionToStringInfo (CCSSettingStringInfo *forString,
98 			       const char *name,
99 			       const char *value)
100 {
101     CCSStrRestriction *restriction;
102 
103     restriction = (CCSStrRestriction *) calloc (1, sizeof (CCSStrRestriction));
104     if (restriction)
105     {
106 	restriction->name = strdup (name);
107 	restriction->value = strdup (value);
108 	forString->restriction =
109 	    ccsStrRestrictionListAppend (forString->restriction,
110 					 restriction);
111     }
112 }
113 
114 static void
ccsAddRestrictionToStringExtension(CCSStrExtension * extension,const char * name,const char * value)115 ccsAddRestrictionToStringExtension (CCSStrExtension *extension,
116 				    const char *name,
117 				    const char *value)
118 {
119     CCSStrRestriction *restriction;
120 
121     restriction = (CCSStrRestriction *) calloc (1, sizeof (CCSStrRestriction));
122     if (restriction)
123     {
124 	restriction->name = strdup (name);
125 	restriction->value = strdup (value);
126 	extension->restriction =
127 	    ccsStrRestrictionListAppend (extension->restriction, restriction);
128     }
129 }
130 
131 
132 #ifdef USE_PROTOBUF
133 
134 static void
initBoolValuePB(CCSSettingValue * v,const GenericValueMetadata & value)135 initBoolValuePB (CCSSettingValue * v,
136 		 const GenericValueMetadata & value)
137 {
138     v->value.asBool = FALSE;
139 
140     if (value.has_bool_value ())
141     {
142 	v->value.asBool = value.bool_value ();
143     }
144 }
145 
146 static void
initIntValuePB(CCSSettingValue * v,CCSSettingInfo * i,const GenericValueMetadata & value)147 initIntValuePB (CCSSettingValue * v,
148 		CCSSettingInfo * i,
149 		const GenericValueMetadata & value)
150 {
151     v->value.asInt = (i->forInt.min + i->forInt.max) / 2;
152 
153     if (value.has_int_value ())
154     {
155 	int val = value.int_value ();
156 	if (val >= i->forInt.min && val <= i->forInt.max)
157 	    v->value.asInt = val;
158     }
159 }
160 
161 static void
initFloatValuePB(CCSSettingValue * v,CCSSettingInfo * i,const GenericValueMetadata & value)162 initFloatValuePB (CCSSettingValue * v,
163 		  CCSSettingInfo * i,
164 		  const GenericValueMetadata & value)
165 {
166     v->value.asFloat = (i->forFloat.min + i->forFloat.max) / 2;
167 
168     if (value.has_float_value ())
169     {
170 	float val = value.float_value ();
171 	if (val >= i->forFloat.min && val <= i->forFloat.max)
172 	    v->value.asFloat = val;
173     }
174 }
175 
176 static void
initStringValuePB(CCSSettingValue * v,CCSSettingInfo * i,const GenericValueMetadata & value)177 initStringValuePB (CCSSettingValue * v,
178 		   CCSSettingInfo * i,
179 		   const GenericValueMetadata & value)
180 {
181     free (v->value.asString);
182 
183     if (value.has_string_value ())
184 	v->value.asString = strdup (value.string_value ().c_str ());
185     else
186 	v->value.asString = strdup ("");
187 }
188 
189 static void
initColorValuePB(CCSSettingValue * v,const GenericValueMetadata & value)190 initColorValuePB (CCSSettingValue * v,
191 		  const GenericValueMetadata & value)
192 {
193     memset (&v->value.asColor, 0, sizeof (v->value.asColor));
194     v->value.asColor.color.alpha = 0xffff;
195 
196     if (!value.has_color_value ())
197 	return;
198 
199     const OptionMetadata::ColorValue &val = value.color_value ();
200 
201     if (val.has_red ())
202     {
203 	int color = strtol ((char *) val.red ().c_str (), NULL, 0);
204 
205 	v->value.asColor.color.red = MAX (0, MIN (0xffff, color));
206     }
207 
208     if (val.has_green ())
209     {
210 	int color = strtol ((char *) val.green ().c_str (), NULL, 0);
211 
212 	v->value.asColor.color.green = MAX (0, MIN (0xffff, color));
213     }
214 
215     if (val.has_blue ())
216     {
217 	int color = strtol ((char *) val.blue ().c_str (), NULL, 0);
218 
219 	v->value.asColor.color.blue = MAX (0, MIN (0xffff, color));
220     }
221 
222     if (val.has_alpha ())
223     {
224 	int color = strtol ((char *) val.alpha ().c_str (), NULL, 0);
225 
226 	v->value.asColor.color.alpha = MAX (0, MIN (0xffff, color));
227     }
228 }
229 
230 static void
initMatchValuePB(CCSSettingValue * v,const GenericValueMetadata & value)231 initMatchValuePB (CCSSettingValue * v,
232 		  const GenericValueMetadata & value)
233 {
234     free (v->value.asMatch);
235 
236     if (value.has_string_value ())
237 	v->value.asMatch = strdup (value.string_value ().c_str ());
238     else
239 	v->value.asMatch = strdup ("");
240 }
241 
242 static void
initKeyValuePB(CCSSettingValue * v,CCSSettingInfo * i,const GenericValueMetadata & value)243 initKeyValuePB (CCSSettingValue * v,
244 		CCSSettingInfo * i,
245 		const GenericValueMetadata & value)
246 {
247     memset (&v->value.asKey, 0, sizeof (v->value.asKey));
248 
249     if (value.has_string_value ())
250     {
251 	const char * val = value.string_value ().c_str ();
252 
253 	if (strcasecmp (val, "disabled"))
254 	{
255 	    ccsStringToKeyBinding (val, &v->value.asKey);
256 	}
257     }
258 }
259 
260 static void
initButtonValuePB(CCSSettingValue * v,CCSSettingInfo * i,const GenericValueMetadata & value)261 initButtonValuePB (CCSSettingValue * v,
262 		   CCSSettingInfo * i,
263 		   const GenericValueMetadata & value)
264 {
265     memset (&v->value.asButton, 0, sizeof (v->value.asButton));
266 
267     if (value.has_string_value ())
268     {
269 	const char * val = value.string_value ().c_str ();
270 
271 	if (strcasecmp (val, "disabled"))
272 	{
273 	    ccsStringToButtonBinding (val, &v->value.asButton);
274 	}
275     }
276 }
277 
278 static void
initEdgeValuePB(CCSSettingValue * v,CCSSettingInfo * i,const GenericValueMetadata & value)279 initEdgeValuePB (CCSSettingValue * v,
280 		 CCSSettingInfo * i,
281 		 const GenericValueMetadata & value)
282 {
283     v->value.asEdge = 0;
284 
285     if (value.has_edge_value ())
286 	v->value.asEdge = value.edge_value ();
287 }
288 
289 static void
initBellValuePB(CCSSettingValue * v,CCSSettingInfo * i,const GenericValueMetadata & value)290 initBellValuePB (CCSSettingValue * v,
291 		 CCSSettingInfo * i,
292 		 const GenericValueMetadata & value)
293 {
294     v->value.asBell = FALSE;
295 
296     if (value.has_bool_value ())
297 	v->value.asBell = value.bool_value ();
298 }
299 
300 static void
initListValuePB(CCSSettingValue * v,CCSSettingInfo * i,const OptionMetadata & option)301 initListValuePB (CCSSettingValue * v,
302 		 CCSSettingInfo * i,
303 		 const OptionMetadata & option)
304 {
305     int num, j;
306 
307     num = option.default_value_size ();
308 
309     if (num)
310     {
311 	for (j = 0; j < num; j++)
312 	{
313 	    CCSSettingValue *val;
314 	    val = (CCSSettingValue *) calloc (1, sizeof (CCSSettingValue));
315 	    if (!val)
316 		continue;
317 
318 	    val->parent = v->parent;
319 	    val->isListChild = TRUE;
320 
321 	    switch (i->forList.listType)
322 	    {
323 	    case TypeBool:
324 		initBoolValuePB (val, option.default_value (j));
325 		break;
326 	    case TypeInt:
327 		initIntValuePB (val, i->forList.listInfo,
328 				option.default_value (j));
329 		break;
330 	    case TypeFloat:
331 		initFloatValuePB (val, i->forList.listInfo,
332 				  option.default_value (j));
333 		break;
334 	    case TypeString:
335 		initStringValuePB (val, i->forList.listInfo,
336 				   option.default_value (j));
337 		break;
338 	    case TypeColor:
339 		initColorValuePB (val, option.default_value (j));
340 		break;
341 	    case TypeKey:
342 		initKeyValuePB (val, i->forList.listInfo,
343 				option.default_value (j));
344 		break;
345 	    case TypeButton:
346 		initButtonValuePB (val, i->forList.listInfo,
347 				   option.default_value (j));
348 		break;
349 	    case TypeEdge:
350 		initEdgeValuePB (val, i->forList.listInfo,
351 				 option.default_value (j));
352 		break;
353 	    case TypeBell:
354 		initBellValuePB (val, i->forList.listInfo,
355 				 option.default_value (j));
356 		break;
357 	    case TypeMatch:
358 		initMatchValuePB (val, option.default_value (j));
359 	    default:
360 		break;
361 	    }
362 	    v->value.asList = ccsSettingValueListAppend (v->value.asList, val);
363 	}
364     }
365 }
366 
367 static void
initIntInfoPB(CCSSettingInfo * i,const OptionMetadata & option)368 initIntInfoPB (CCSSettingInfo * i, const OptionMetadata & option)
369 {
370     int num, j;
371     i->forInt.min = MINSHORT;
372     i->forInt.max = MAXSHORT;
373     i->forInt.desc = NULL;
374 
375     if (option.has_int_min ())
376 	i->forInt.min = option.int_min ();
377 
378     if (option.has_int_max ())
379 	i->forInt.max = option.int_max ();
380 
381     if (!basicMetadata)
382     {
383 	num = option.int_desc_size ();
384 	for (j = 0; j < num; j++)
385 	{
386 	    const OptionMetadata::IntDescription & intDescMetadata =
387 		option.int_desc (j);
388 
389 	    int val = intDescMetadata.value ();
390 
391 	    if (val >= i->forInt.min && val <= i->forInt.max)
392 	    {
393 		CCSIntDesc *intDesc;
394 
395 		intDesc = (CCSIntDesc *) calloc (1, sizeof (CCSIntDesc));
396 		if (intDesc)
397 		{
398 		    intDesc->name = strdup (intDescMetadata.name ().c_str ());
399 		    intDesc->value = val;
400 		    i->forInt.desc =
401 			ccsIntDescListAppend (i->forInt.desc, intDesc);
402 		}
403 	    }
404 	}
405     }
406 }
407 
408 static void
initFloatInfoPB(CCSSettingInfo * i,const OptionMetadata & option)409 initFloatInfoPB (CCSSettingInfo * i, const OptionMetadata & option)
410 {
411     i->forFloat.min = MINSHORT;
412     i->forFloat.max = MAXSHORT;
413     i->forFloat.precision = 0.1f;
414 
415     if (option.has_float_min ())
416 	i->forFloat.min = option.float_min ();
417 
418     if (option.has_float_max ())
419 	i->forFloat.max = option.float_max ();
420 
421     if (option.precision ())
422 	i->forFloat.precision = option.precision ();
423 }
424 
425 static void
initStringInfoPB(CCSSettingInfo * i,const OptionMetadata & option)426 initStringInfoPB (CCSSettingInfo * i, const OptionMetadata & option)
427 {
428     int num, j;
429     i->forString.restriction = NULL;
430     i->forString.sortStartsAt = -1;
431     i->forString.extensible = FALSE;
432 
433     if (!basicMetadata)
434     {
435 	if (option.has_extensible () && option.extensible ())
436 	    i->forString.extensible = TRUE;
437 
438 	if (option.has_sort_start ())
439 	    i->forString.sortStartsAt = option.sort_start ();
440 
441 	num = option.str_restriction_size ();
442 	for (j = 0; j < num; j++)
443 	{
444 	    const OptionMetadata::StringRestriction &
445 		restrictionMetadata = option.str_restriction (j);
446 
447 	    const char *value = restrictionMetadata.value ().c_str ();
448 	    const char *name = restrictionMetadata.name ().c_str ();
449 
450 	    ccsAddRestrictionToStringInfo (&i->forString, value, name);
451 	}
452     }
453 }
454 
455 static void
initListInfoPB(CCSSettingInfo * i,const OptionMetadata & option)456 initListInfoPB (CCSSettingInfo * i, const OptionMetadata & option)
457 {
458     CCSSettingInfo *info;
459 
460     i->forList.listType = TypeBool;
461     i->forList.listInfo = NULL;
462 
463     if (option.has_list_type ())
464     {
465 	i->forList.listType = (CCSSettingType) option.list_type ();
466     }
467     switch (i->forList.listType)
468     {
469     case TypeInt:
470 	{
471 	    info = (CCSSettingInfo *) calloc (1, sizeof (CCSSettingInfo));
472 	    if (info)
473 		initIntInfoPB (info, option);
474 	    i->forList.listInfo = info;
475 	}
476 	break;
477     case TypeFloat:
478 	{
479 	    info = (CCSSettingInfo *) calloc (1, sizeof (CCSSettingInfo));
480 	    if (info)
481 		initFloatInfoPB (info, option);
482 	    i->forList.listInfo = info;
483 	}
484 	break;
485     case TypeString:
486 	{
487 	    info = (CCSSettingInfo *) calloc (1, sizeof (CCSSettingInfo));
488 	    if (info)
489 		initStringInfoPB (info, option);
490 	    i->forList.listInfo = info;
491 	}
492 	break;
493     default:
494 	break;
495     }
496 }
497 
498 static void
initActionInfoPB(CCSSettingInfo * i,const OptionMetadata & option)499 initActionInfoPB (CCSSettingInfo * i, const OptionMetadata & option)
500 {
501     i->forAction.internal = FALSE;
502 
503     if (option.has_internal () && option.internal ())
504 	i->forAction.internal = TRUE;
505 }
506 
507 static void
addOptionForPluginPB(CCSPlugin * plugin,const char * name,Bool isScreen,unsigned int screen,const StringList & groups,const StringList & subgroups,const OptionMetadata & option)508 addOptionForPluginPB (CCSPlugin * plugin,
509 		      const char * name,
510 		      Bool isScreen,
511 		      unsigned int screen,
512 		      const StringList & groups,
513 		      const StringList & subgroups,
514 		      const OptionMetadata & option)
515 {
516     CCSSetting *setting;
517 
518     if (ccsFindSetting (plugin, name, isScreen, screen))
519     {
520 	fprintf (stderr, "[ERROR]: Option \"%s\" already defined\n", name);
521 	return;
522     }
523 
524     setting = (CCSSetting *) calloc (1, sizeof (CCSSetting));
525     if (!setting)
526 	return;
527 
528     setting->parent = plugin;
529     setting->isScreen = isScreen;
530     setting->screenNum = screen;
531     setting->isDefault = TRUE;
532     setting->name = strdup (name);
533 
534     if (!basicMetadata)
535     {
536 	setting->shortDesc =
537 	    strdup (option.has_short_desc () ?
538 		    option.short_desc ().c_str () :
539 		    name);
540 	setting->longDesc =
541 	    strdup (option.has_long_desc () ?
542 		    option.long_desc ().c_str () :
543 		    name);
544 	setting->hints = strdup (option.has_hints () ?
545 				 option.hints ().c_str () :
546 				 name);
547 	setting->group =
548 	    strdup (option.group_id () >= 0 ?
549 		    groups.Get (option.group_id ()).c_str () :
550 		    "");
551 	setting->subGroup =
552 	    strdup (option.subgroup_id () >= 0 ?
553 		    subgroups.Get (option.subgroup_id ()).c_str () :
554 		    "");
555     }
556     else
557     {
558 	setting->shortDesc = strdup (name);
559 	setting->longDesc  = strdup ("");
560 	setting->hints     = strdup ("");
561 	setting->group     = strdup ("");
562 	setting->subGroup  = strdup ("");
563     }
564 
565     setting->type = (CCSSettingType) option.type ();
566     setting->value = &setting->defaultValue;
567     setting->defaultValue.parent = setting;
568 
569     switch (setting->type)
570     {
571     case TypeInt:
572 	initIntInfoPB (&setting->info, option);
573 	break;
574     case TypeFloat:
575 	initFloatInfoPB (&setting->info, option);
576 	break;
577     case TypeString:
578 	initStringInfoPB (&setting->info, option);
579 	break;
580     case TypeList:
581 	initListInfoPB (&setting->info, option);
582 	break;
583     case TypeKey:
584     case TypeButton:
585     case TypeEdge:
586     case TypeBell:
587 	initActionInfoPB (&setting->info, option);
588 	break;
589     case TypeAction: // do nothing and fall through
590     default:
591 	break;
592     }
593 
594     if (option.default_value_size () > 0)
595     {
596 	switch (setting->type)
597 	{
598 	case TypeInt:
599 	    initIntValuePB (&setting->defaultValue, &setting->info,
600 			    option.default_value (0));
601 	    break;
602 	case TypeBool:
603 	    initBoolValuePB (&setting->defaultValue, option.default_value (0));
604 	    break;
605 	case TypeFloat:
606 	    initFloatValuePB (&setting->defaultValue, &setting->info,
607 			      option.default_value (0));
608 	    break;
609 	case TypeString:
610 	    initStringValuePB (&setting->defaultValue, &setting->info,
611 			       option.default_value (0));
612 	    break;
613 	case TypeColor:
614 	    initColorValuePB (&setting->defaultValue, option.default_value (0));
615 	    break;
616 	case TypeKey:
617 	    initKeyValuePB (&setting->defaultValue, &setting->info,
618 			    option.default_value (0));
619 	    break;
620 	case TypeButton:
621 	    initButtonValuePB (&setting->defaultValue, &setting->info,
622 			       option.default_value (0));
623 	    break;
624 	case TypeEdge:
625 	    initEdgeValuePB (&setting->defaultValue, &setting->info,
626 			     option.default_value (0));
627 	    break;
628 	case TypeBell:
629 	    initBellValuePB (&setting->defaultValue, &setting->info,
630 			     option.default_value (0));
631 	    break;
632 	case TypeMatch:
633 	    initMatchValuePB (&setting->defaultValue,
634 			      option.default_value (0));
635 	    break;
636 	case TypeList:
637 	    initListValuePB (&setting->defaultValue, &setting->info,
638 			     option);
639 	    break;
640 	case TypeAction: // do nothing and fall through
641 	default:
642 	    break;
643 	}
644     }
645     else
646     {
647 	/* if we have no set defaults, we have at least to set
648 	   the string defaults to empty strings */
649 	switch (setting->type)
650 	{
651 	case TypeString:
652 	    setting->defaultValue.value.asString = strdup ("");
653 	    break;
654 	case TypeMatch:
655 	    setting->defaultValue.value.asMatch = strdup ("");
656 	    break;
657 	default:
658 	    break;
659 	}
660     }
661 
662     PLUGIN_PRIV (plugin);
663 
664     pPrivate->settings = ccsSettingListAppend (pPrivate->settings, setting);
665 }
666 
667 static void
addOptionFromPB(CCSPlugin * plugin,Bool isScreen,const StringList & groups,const StringList & subgroups,const OptionMetadata & option)668 addOptionFromPB (CCSPlugin * plugin,
669 		 Bool isScreen,
670 		 const StringList & groups,
671 		 const StringList & subgroups,
672 		 const OptionMetadata & option)
673 {
674     const char *name;
675     Bool readonly = FALSE;
676 
677     name = option.name ().c_str ();
678 
679     readonly = option.has_read_only () && option.read_only ();
680 
681     if (!strlen (name) || readonly)
682 	return;
683 
684     if (isScreen)
685     {
686 	for (unsigned i = 0; i < plugin->context->numScreens; i++)
687 	    addOptionForPluginPB (plugin, name, TRUE,
688 				  plugin->context->screens[i],
689 				  groups, subgroups, option);
690     }
691     else
692 	addOptionForPluginPB (plugin, name, FALSE, 0, groups, subgroups, option);
693 }
694 
695 static void
initOptionsFromPB(CCSPlugin * plugin,const PluginMetadata & pluginPB)696 initOptionsFromPB (CCSPlugin * plugin,
697 		   const PluginMetadata & pluginPB)
698 {
699     int numOpt, i;
700 
701     if (pluginPB.has_display ())
702     {
703 	const ScreenMetadata &displayPB = pluginPB.display ();
704 
705 	// Display options
706 	numOpt = displayPB.option_size ();
707 	for (i = 0; i < numOpt; i++)
708 	    addOptionFromPB (plugin, FALSE,
709 			     displayPB.group_desc (),
710 			     displayPB.subgroup_desc (),
711 			     displayPB.option (i));
712     }
713 
714     if (pluginPB.has_screen ())
715     {
716 	const ScreenMetadata &screenPB = pluginPB.screen ();
717 
718 	// Screen options
719 	numOpt = screenPB.option_size ();
720 	for (i = 0; i < numOpt; i++)
721 	    addOptionFromPB (plugin, TRUE,
722 			     screenPB.group_desc (),
723 			     screenPB.subgroup_desc (),
724 			     screenPB.option (i));
725     }
726 }
727 
728 static void
addStringsFromPB(CCSStringList * list,const StringList & strings)729 addStringsFromPB (CCSStringList * list,
730 		  const StringList & strings)
731 {
732     StringList::const_iterator it;
733 
734     for (it = strings.begin (); it != strings.end (); it++)
735     {
736 	const char *value = (*it).c_str ();
737 
738 	if (strlen (value))
739 	    *list = ccsStringListAppend (*list, strdup (value));
740     }
741 }
742 
743 static void
addStringExtensionFromPB(CCSPlugin * plugin,const ExtensionMetadata & extensionPB)744 addStringExtensionFromPB (CCSPlugin * plugin,
745 			  const ExtensionMetadata & extensionPB)
746 {
747     int j;
748     CCSStrExtension *extension;
749 
750     extension = (CCSStrExtension *) calloc (1, sizeof (CCSStrExtension));
751     if (!extension)
752 	return;
753 
754     extension->isScreen = !(extensionPB.has_display () &&
755 			    extensionPB.display ());
756 
757     extension->restriction = NULL;
758 
759     extension->basePlugin = strdup (extensionPB.base_plugin ().c_str ());
760 
761     addStringsFromPB (&extension->baseSettings,
762 		      extensionPB.base_option ());
763 
764     int numRestrictions = extensionPB.str_restriction_size ();
765     if (!numRestrictions)
766     {
767 	free (extension);
768 	return;
769     }
770 
771     for (j = 0; j < numRestrictions; j++)
772     {
773 	const OptionMetadata::StringRestriction & restrictionPB =
774 	    extensionPB.str_restriction (j);
775 
776 	const char *value = restrictionPB.value ().c_str ();
777 	const char *name  = restrictionPB.name ().c_str ();
778 
779 	ccsAddRestrictionToStringExtension (extension, name, value);
780     }
781 
782     PLUGIN_PRIV (plugin);
783 
784     pPrivate->stringExtensions =
785 	ccsStrExtensionListAppend (pPrivate->stringExtensions, extension);
786 }
787 
788 static void
initStringExtensionsFromPB(CCSPlugin * plugin,const PluginMetadata & pluginPB)789 initStringExtensionsFromPB (CCSPlugin * plugin,
790 			    const PluginMetadata & pluginPB)
791 {
792     int numExtensions, i;
793 
794     numExtensions = pluginPB.extension_size ();
795     for (i = 0; i < numExtensions; i++)
796 	addStringExtensionFromPB (plugin, pluginPB.extension (i));
797 }
798 
799 static void
initRulesFromPB(CCSPlugin * plugin,const PluginInfoMetadata & pluginInfoPB)800 initRulesFromPB (CCSPlugin * plugin, const PluginInfoMetadata & pluginInfoPB)
801 {
802     addStringsFromPB (&plugin->providesFeature, pluginInfoPB.feature ());
803 
804     if (!pluginInfoPB.has_deps ())
805 	return;
806 
807     const DependenciesMetadata & deps = pluginInfoPB.deps ();
808 
809     addStringsFromPB (&plugin->loadAfter, deps.after_plugin ());
810     addStringsFromPB (&plugin->loadBefore, deps.before_plugin ());
811     addStringsFromPB (&plugin->requiresPlugin, deps.require_plugin ());
812     addStringsFromPB (&plugin->requiresFeature, deps.require_feature ());
813     addStringsFromPB (&plugin->conflictPlugin, deps.conflict_plugin ());
814     addStringsFromPB (&plugin->conflictFeature, deps.conflict_feature ());
815 }
816 
817 static void
addPluginFromPB(CCSContext * context,const PluginInfoMetadata & pluginInfoPB,char * file,char * xmlFile)818 addPluginFromPB (CCSContext * context,
819 		 const PluginInfoMetadata & pluginInfoPB,
820 		 char *file,
821 		 char *xmlFile)
822 {
823     const char *name;
824     CCSPlugin *plugin;
825     CCSPluginPrivate *pPrivate;
826 
827     name = pluginInfoPB.name ().c_str ();
828 
829     if (!strlen (name))
830 	return;
831 
832     if (ccsFindPlugin (context, name))
833 	return;
834 
835     if (!strcmp (name, "ini") || !strcmp (name, "gconf") ||
836 	!strcmp (name, "ccp") || !strcmp (name, "kconfig"))
837 	return;
838 
839     plugin = (CCSPlugin *) calloc (1, sizeof (CCSPlugin));
840     if (!plugin)
841 	return;
842 
843     pPrivate = (CCSPluginPrivate *) calloc (1, sizeof (CCSPluginPrivate));
844     if (!pPrivate)
845     {
846 	free (plugin);
847 	return;
848     }
849     pPrivate->loaded = FALSE;
850 
851     plugin->ccsPrivate = (void *) pPrivate;
852 
853     if (file)
854 	pPrivate->pbFilePath = strdup (file);
855 
856     if (xmlFile)
857     {
858 	pPrivate->xmlFile = strdup (xmlFile);
859 	asprintf (&pPrivate->xmlPath, "/compiz/plugin[@name = '%s']", name);
860     }
861 
862     plugin->context = context;
863     plugin->name = strdup (name);
864 
865     if (!basicMetadata)
866     {
867 	plugin->shortDesc =
868 	    strdup (pluginInfoPB.has_short_desc () ?
869 		    pluginInfoPB.short_desc ().c_str () :
870 		    name);
871 	plugin->longDesc =
872 	    strdup (pluginInfoPB.has_long_desc () ?
873 		    pluginInfoPB.long_desc ().c_str () :
874 		    name);
875 	plugin->category = strdup (pluginInfoPB.has_category () ?
876 				   pluginInfoPB.category ().c_str () :
877 				   "");
878     }
879     else
880     {
881 	plugin->shortDesc = strdup (name);
882 	plugin->longDesc  = strdup (name);
883 	plugin->category  = strdup ("");
884     }
885 
886     initRulesFromPB (plugin, pluginInfoPB);
887 
888     context->plugins = ccsPluginListAppend (context->plugins, plugin);
889 }
890 
891 static void
addCoreSettingsFromPB(CCSContext * context,const PluginInfoMetadata & pluginInfoPB,char * file,char * xmlFile)892 addCoreSettingsFromPB (CCSContext * context,
893 		       const PluginInfoMetadata & pluginInfoPB,
894 		       char *file,
895 		       char *xmlFile)
896 {
897     CCSPlugin *plugin;
898     CCSPluginPrivate *pPrivate;
899 
900     if (ccsFindPlugin (context, "core"))
901 	return;
902 
903     plugin = (CCSPlugin*) calloc (1, sizeof (CCSPlugin));
904     if (!plugin)
905 	return;
906 
907     pPrivate = (CCSPluginPrivate *) calloc (1, sizeof (CCSPluginPrivate));
908     if (!pPrivate)
909     {
910 	free (plugin);
911 	return;
912     }
913 
914     plugin->ccsPrivate = (void *) pPrivate;
915 
916     if (file)
917 	pPrivate->pbFilePath = strdup (file);
918 
919     if (xmlFile)
920     {
921 	pPrivate->xmlFile = strdup (xmlFile);
922 	pPrivate->xmlPath = strdup ("/compiz/core");
923     }
924 
925     plugin->context = context;
926     plugin->name = strdup ("core");
927     plugin->category = strdup ("General");
928 
929     if (!basicMetadata)
930     {
931 	plugin->shortDesc =
932 	    strdup (pluginInfoPB.has_short_desc () ?
933 		    pluginInfoPB.short_desc ().c_str () :
934 		    "General Options");
935 
936 	    plugin->longDesc =
937 	    strdup (pluginInfoPB.has_long_desc () ?
938 		    pluginInfoPB.long_desc ().c_str () :
939 		    "General Compiz Options");
940     }
941     else
942     {
943 	plugin->shortDesc = strdup ("General Options");
944 	plugin->longDesc  = strdup ("General Compiz Options");
945     }
946 
947     initRulesFromPB (plugin, pluginInfoPB);
948     context->plugins = ccsPluginListAppend (context->plugins, plugin);
949 }
950 
951 #endif
952 
953 
954 static int
pluginNameFilter(const struct dirent * name)955 pluginNameFilter (const struct dirent *name)
956 {
957     int length = strlen (name->d_name);
958 
959     if (length < 7)
960 	return 0;
961 
962     if (strncmp (name->d_name, "lib", 3) ||
963 	strncmp (name->d_name + length - 3, ".so", 3))
964 	return 0;
965 
966     return 1;
967 }
968 
969 static int
pluginXMLFilter(const struct dirent * name)970 pluginXMLFilter (const struct dirent *name)
971 {
972     int length = strlen (name->d_name);
973 
974     if (length < 5)
975 	return 0;
976 
977     if (strncmp (name->d_name + length - 4, ".xml", 4))
978 	return 0;
979 
980     return 1;
981 }
982 
983 
984 /* XML parsing */
985 
986 static CCSSettingType
getOptionType(const char * name)987 getOptionType (const char *name)
988 {
989     static struct _TypeMap
990     {
991 	const char *name;
992 	CCSSettingType type;
993     } map[] = {
994 	{ "bool", TypeBool },
995 	{ "int", TypeInt },
996 	{ "float", TypeFloat },
997 	{ "string", TypeString },
998 	{ "color", TypeColor },
999 	{ "action", TypeAction },
1000 	{ "key", TypeKey },
1001 	{ "button", TypeButton },
1002 	{ "edge", TypeEdge },
1003 	{ "bell", TypeBell },
1004 	{ "match", TypeMatch },
1005 	{ "list", TypeList }
1006     };
1007     for (unsigned i = 0; i < sizeof (map) / sizeof (map[0]); i++)
1008 	if (strcasecmp (name, map[i].name) == 0)
1009 	    return map[i].type;
1010 
1011     return TypeNum;
1012 }
1013 
1014 static char *
getStringFromXPath(xmlDoc * doc,xmlNode * base,const char * path)1015 getStringFromXPath (xmlDoc * doc, xmlNode * base, const char *path)
1016 {
1017     xmlXPathObjectPtr xpathObj;
1018     xmlXPathContextPtr xpathCtx;
1019     char *rv = NULL;
1020 
1021     xpathCtx = xmlXPathNewContext (doc);
1022     if (!xpathCtx)
1023 	return NULL;
1024 
1025     if (base)
1026 	xpathCtx->node = base;
1027 
1028     xpathObj = xmlXPathEvalExpression (BAD_CAST path, xpathCtx);
1029 
1030     if (!xpathObj)
1031     {
1032 	xmlXPathFreeContext (xpathCtx);
1033 	return NULL;
1034     }
1035 
1036     xpathObj = xmlXPathConvertString (xpathObj);
1037 
1038     if (xpathObj->type == XPATH_STRING && xpathObj->stringval
1039 	&& strlen ((char *) xpathObj->stringval))
1040     {
1041 	rv = strdup ((char *) xpathObj->stringval);
1042     }
1043 
1044     xmlXPathFreeObject (xpathObj);
1045     xmlXPathFreeContext (xpathCtx);
1046     return rv;
1047 }
1048 
1049 static xmlNode **
getNodesFromXPath(xmlDoc * doc,xmlNode * base,const char * path,int * num)1050 getNodesFromXPath (xmlDoc * doc, xmlNode * base, const char *path, int *num)
1051 {
1052     xmlXPathObjectPtr xpathObj;
1053     xmlXPathContextPtr xpathCtx;
1054     xmlNode **rv = NULL;
1055     int size;
1056     int i;
1057 
1058     *num = 0;
1059 
1060     xpathCtx = xmlXPathNewContext (doc);
1061     if (!xpathCtx)
1062 	return NULL;
1063 
1064     if (base)
1065 	xpathCtx->node = base;
1066 
1067     xpathObj = xmlXPathEvalExpression (BAD_CAST path, xpathCtx);
1068     if (!xpathObj)
1069     {
1070 	xmlXPathFreeContext (xpathCtx);
1071 	return NULL;
1072     }
1073 
1074     size = (xpathObj->nodesetval) ? xpathObj->nodesetval->nodeNr : 0;
1075     if (!size)
1076     {
1077 	xmlXPathFreeObject (xpathObj);
1078 	xmlXPathFreeContext (xpathCtx);
1079 	return NULL;
1080     }
1081 
1082     rv = (xmlNode **) malloc (size * sizeof (xmlNode *));
1083     if (!rv)
1084     {
1085 	xmlXPathFreeObject (xpathObj);
1086 	xmlXPathFreeContext (xpathCtx);
1087 	return NULL;
1088     }
1089     *num = size;
1090 
1091     for (i = 0; i < size; i++)
1092 	rv[i] = xpathObj->nodesetval->nodeTab[i];
1093 
1094     xmlXPathFreeObject (xpathObj);
1095     xmlXPathFreeContext (xpathCtx);
1096 
1097     return rv;
1098 }
1099 
1100 static Bool
nodeExists(xmlNode * node,const char * path)1101 nodeExists (xmlNode * node, const char *path)
1102 {
1103     xmlNode **nodes = NULL;
1104     int num;
1105     nodes = getNodesFromXPath (node->doc, node, path, &num);
1106 
1107     if (num)
1108     {
1109 	free (nodes);
1110 	return TRUE;
1111     }
1112 
1113     return FALSE;
1114 }
1115 
1116 static char *
stringFromNodeDef(xmlNode * node,const char * path,const char * def)1117 stringFromNodeDef (xmlNode * node, const char *path, const char *def)
1118 {
1119     char *val;
1120     char *rv = NULL;
1121 
1122     val = getStringFromXPath (node->doc, node, path);
1123 
1124     if (val)
1125     {
1126 	rv = strdup (val);
1127 	free (val);
1128     }
1129     else if (def)
1130 	rv = strdup (def);
1131 
1132     return rv;
1133 }
1134 
1135 static char *
stringFromNodeDefTrans(xmlNode * node,const char * path,const char * def)1136 stringFromNodeDefTrans (xmlNode * node, const char *path, const char *def)
1137 {
1138     const char *lang = getLocale ();
1139     char newPath[1024];
1140     char *rv = NULL;
1141 
1142     if (!lang || !strlen (lang))
1143 	return stringFromNodeDef (node, path, def);
1144 
1145     snprintf (newPath, 1023, "%s[lang('%s')]", path, lang);
1146     rv = stringFromNodeDef (node, newPath, NULL);
1147     if (rv)
1148 	return rv;
1149 
1150     snprintf (newPath, 1023, "%s[lang(substring-before('%s','.'))]", path, lang);
1151     rv = stringFromNodeDef (node, newPath, NULL);
1152     if (rv)
1153 	return rv;
1154 
1155     snprintf (newPath, 1023, "%s[lang(substring-before('%s','_'))]", path, lang);
1156     rv = stringFromNodeDef (node, newPath, NULL);
1157     if (rv)
1158 	return rv;
1159 
1160     snprintf (newPath, 1023, "%s[lang('C')]", path);
1161     rv = stringFromNodeDef (node, newPath, NULL);
1162     if (rv)
1163 	return rv;
1164 
1165     return stringFromNodeDef (node, path, def);
1166 }
1167 
1168 static void
initBoolValue(CCSSettingValue * v,xmlNode * node,void * valuePBv)1169 initBoolValue (CCSSettingValue * v,
1170 	       xmlNode * node,
1171 	       void * valuePBv)
1172 {
1173     char *value;
1174 
1175     v->value.asBool = FALSE;
1176 
1177     value = getStringFromXPath (node->doc, node, "child::text()");
1178 
1179     if (value)
1180     {
1181 	if (strcasecmp ((char *) value, "true") == 0)
1182 	{
1183 	    v->value.asBool = TRUE;
1184 #ifdef USE_PROTOBUF
1185 	    if (valuePBv)
1186 		((GenericValueMetadata *) valuePBv)->set_bool_value (TRUE);
1187 #endif
1188 	}
1189 	free (value);
1190     }
1191 }
1192 
1193 static void
initIntValue(CCSSettingValue * v,CCSSettingInfo * i,xmlNode * node,void * valuePBv)1194 initIntValue (CCSSettingValue * v,
1195 	      CCSSettingInfo * i,
1196 	      xmlNode * node,
1197 	      void * valuePBv)
1198 {
1199     char *value;
1200 
1201     v->value.asInt = (i->forInt.min + i->forInt.max) / 2;
1202 
1203     value = getStringFromXPath (node->doc, node, "child::text()");
1204 
1205     if (value)
1206     {
1207 	int val = strtol ((char *) value, NULL, 0);
1208 
1209 	if (val >= i->forInt.min && val <= i->forInt.max)
1210 	{
1211 	    v->value.asInt = val;
1212 #ifdef USE_PROTOBUF
1213 	    if (valuePBv)
1214 		((GenericValueMetadata *) valuePBv)->set_int_value (val);
1215 #endif
1216 	}
1217 
1218 	free (value);
1219     }
1220 }
1221 
1222 static void
initFloatValue(CCSSettingValue * v,CCSSettingInfo * i,xmlNode * node,void * valuePBv)1223 initFloatValue (CCSSettingValue * v,
1224 		CCSSettingInfo * i,
1225 		xmlNode * node,
1226 		void * valuePBv)
1227 {
1228     char *value;
1229     char *loc;
1230 
1231     v->value.asFloat = (i->forFloat.min + i->forFloat.max) / 2;
1232 
1233     loc = setlocale (LC_NUMERIC, NULL);
1234     setlocale (LC_NUMERIC, "C");
1235     value = getStringFromXPath (node->doc, node, "child::text()");
1236 
1237     if (value)
1238     {
1239 	float val = strtod ((char *) value, NULL);
1240 
1241 	if (val >= i->forFloat.min && val <= i->forFloat.max)
1242 	{
1243 	    v->value.asFloat = val;
1244 #ifdef USE_PROTOBUF
1245 	    if (valuePBv)
1246 		((GenericValueMetadata *) valuePBv)->set_float_value (val);
1247 #endif
1248 	}
1249 
1250 	free (value);
1251     }
1252 
1253     setlocale (LC_NUMERIC, loc);
1254 }
1255 
1256 static void
initStringValue(CCSSettingValue * v,CCSSettingInfo * i,xmlNode * node,void * valuePBv)1257 initStringValue (CCSSettingValue * v,
1258 		 CCSSettingInfo * i,
1259 		 xmlNode * node,
1260 		 void * valuePBv)
1261 {
1262     char *value;
1263 
1264     value = getStringFromXPath (node->doc, node, "child::text()");
1265 
1266     if (value)
1267     {
1268 	free (v->value.asString);
1269 	v->value.asString = strdup (value);
1270 
1271 #ifdef USE_PROTOBUF
1272 	if (valuePBv)
1273 	    ((GenericValueMetadata *) valuePBv)->set_string_value (value);
1274 #endif
1275 	free (value);
1276     }
1277     else
1278 	v->value.asString = strdup ("");
1279 }
1280 
1281 static void
initColorValue(CCSSettingValue * v,xmlNode * node,void * valuePBv)1282 initColorValue (CCSSettingValue * v, xmlNode * node, void * valuePBv)
1283 {
1284     char *value;
1285 
1286     memset (&v->value.asColor, 0, sizeof (v->value.asColor));
1287     v->value.asColor.color.alpha = 0xffff;
1288 
1289 #ifdef USE_PROTOBUF
1290     OptionMetadata::ColorValue *colorPB = NULL;
1291     if (valuePBv)
1292 	colorPB = ((GenericValueMetadata *) valuePBv)->mutable_color_value ();
1293 #endif
1294 
1295     value = getStringFromXPath (node->doc, node, "red/child::text()");
1296     if (value)
1297     {
1298 	int color = strtol ((char *) value, NULL, 0);
1299 
1300 	v->value.asColor.color.red = MAX (0, MIN (0xffff, color));
1301 #ifdef USE_PROTOBUF
1302 	if (colorPB)
1303 	    colorPB->set_red (value);
1304 #endif
1305 	free (value);
1306     }
1307 
1308     value = getStringFromXPath (node->doc, node, "green/child::text()");
1309     if (value)
1310     {
1311 	int color = strtol ((char *) value, NULL, 0);
1312 
1313 	v->value.asColor.color.green = MAX (0, MIN (0xffff, color));
1314 #ifdef USE_PROTOBUF
1315 	if (colorPB)
1316 	    colorPB->set_green (value);
1317 #endif
1318 	free (value);
1319     }
1320 
1321     value = getStringFromXPath (node->doc, node, "blue/child::text()");
1322     if (value)
1323     {
1324 	int color = strtol ((char *) value, NULL, 0);
1325 
1326 	v->value.asColor.color.blue = MAX (0, MIN (0xffff, color));
1327 #ifdef USE_PROTOBUF
1328 	if (colorPB)
1329 	    colorPB->set_blue (value);
1330 #endif
1331 	free (value);
1332     }
1333 
1334     value = getStringFromXPath (node->doc, node, "alpha/child::text()");
1335     if (value)
1336     {
1337 	int color = strtol (value, NULL, 0);
1338 
1339 	v->value.asColor.color.alpha = MAX (0, MIN (0xffff, color));
1340 #ifdef USE_PROTOBUF
1341 	if (colorPB)
1342 	    colorPB->set_alpha (value);
1343 #endif
1344 	free (value);
1345     }
1346 }
1347 
1348 static void
initMatchValue(CCSSettingValue * v,xmlNode * node,void * valuePBv)1349 initMatchValue (CCSSettingValue * v, xmlNode * node, void * valuePBv)
1350 {
1351     char *value;
1352 
1353     value = getStringFromXPath (node->doc, node, "child::text()");
1354     if (value)
1355     {
1356 	free (v->value.asMatch);
1357 	v->value.asMatch = strdup (value);
1358 
1359 #ifdef USE_PROTOBUF
1360 	if (valuePBv)
1361 	    ((GenericValueMetadata *) valuePBv)->set_string_value (value);
1362 #endif
1363 	free (value);
1364     }
1365     else
1366 	v->value.asMatch = strdup ("");
1367 }
1368 
1369 static void
initKeyValue(CCSSettingValue * v,CCSSettingInfo * i,xmlNode * node,void * valuePBv)1370 initKeyValue (CCSSettingValue * v,
1371 	      CCSSettingInfo * i,
1372 	      xmlNode * node,
1373 	      void * valuePBv)
1374 {
1375     char *value;
1376 
1377     memset (&v->value.asKey, 0, sizeof (v->value.asKey));
1378 
1379     value = getStringFromXPath (node->doc, node, "child::text()");
1380     if (value)
1381     {
1382 #ifdef USE_PROTOBUF
1383 	if (valuePBv)
1384 	    ((GenericValueMetadata *) valuePBv)->set_string_value (value);
1385 #endif
1386 	if (strcasecmp (value, "disabled"))
1387 	{
1388 	    ccsStringToKeyBinding (value, &v->value.asKey);
1389 	}
1390 	free (value);
1391     }
1392 }
1393 
1394 static void
initButtonValue(CCSSettingValue * v,CCSSettingInfo * i,xmlNode * node,void * valuePBv)1395 initButtonValue (CCSSettingValue * v,
1396 		 CCSSettingInfo * i,
1397 		 xmlNode * node,
1398 		 void * valuePBv)
1399 {
1400     char *value;
1401 
1402     memset (&v->value.asButton, 0, sizeof (v->value.asButton));
1403 
1404     value = getStringFromXPath (node->doc, node, "child::text()");
1405     if (value)
1406     {
1407 #ifdef USE_PROTOBUF
1408 	if (valuePBv)
1409 	    ((GenericValueMetadata *) valuePBv)->set_string_value (value);
1410 #endif
1411 	if (strcasecmp (value, "disabled"))
1412 	{
1413 	    ccsStringToButtonBinding (value, &v->value.asButton);
1414 	}
1415 	free (value);
1416     }
1417 }
1418 
1419 static void
initEdgeValue(CCSSettingValue * v,CCSSettingInfo * i,xmlNode * node,void * valuePBv)1420 initEdgeValue (CCSSettingValue * v,
1421 	       CCSSettingInfo * i,
1422 	       xmlNode * node,
1423 	       void * valuePBv)
1424 {
1425     xmlNode **nodes;
1426     char *value;
1427     int k, num;
1428 
1429     v->value.asEdge = 0;
1430 
1431     static const char *edge[] = {
1432 	"Left",
1433 	"Right",
1434 	"Top",
1435 	"Bottom",
1436 	"TopLeft",
1437 	"TopRight",
1438 	"BottomLeft",
1439 	"BottomRight"
1440     };
1441 
1442     nodes = getNodesFromXPath (node->doc, node, "edge", &num);
1443 
1444     for (k = 0; k < num; k++)
1445     {
1446 	value = getStringFromXPath (node->doc, nodes[k], "@name");
1447 	if (value)
1448 	{
1449 	    for (unsigned j = 0; j < sizeof (edge) / sizeof (edge[0]); j++)
1450 	    {
1451 		if (strcasecmp ((char *) value, edge[j]) == 0)
1452 		    v->value.asEdge |= (1 << j);
1453 	    }
1454 	    free (value);
1455 	}
1456     }
1457     if (num)
1458 	free (nodes);
1459 
1460 #ifdef USE_PROTOBUF
1461     if (valuePBv)
1462 	((GenericValueMetadata *) valuePBv)->set_edge_value (v->value.asEdge);
1463 #endif
1464 }
1465 
1466 static void
initBellValue(CCSSettingValue * v,CCSSettingInfo * i,xmlNode * node,void * valuePBv)1467 initBellValue (CCSSettingValue * v,
1468 	       CCSSettingInfo * i,
1469 	       xmlNode * node,
1470 	       void * valuePBv)
1471 {
1472     char *value;
1473 
1474     v->value.asBell = FALSE;
1475 
1476     value = getStringFromXPath (node->doc, node, "child::text()");
1477     if (value)
1478     {
1479 	if (!strcasecmp (value, "true"))
1480 	{
1481 	    v->value.asBell = TRUE;
1482 #ifdef USE_PROTOBUF
1483 	    if (valuePBv)
1484 		((GenericValueMetadata *) valuePBv)->set_bool_value (TRUE);
1485 #endif
1486 	}
1487 	free (value);
1488     }
1489 }
1490 
1491 static void
initListValue(CCSSettingValue * v,CCSSettingInfo * i,xmlNode * node,void * optionPBv)1492 initListValue (CCSSettingValue * v,
1493 	       CCSSettingInfo * i,
1494 	       xmlNode * node,
1495 	       void * optionPBv)
1496 {
1497     xmlNode **nodes;
1498     int num, j;
1499 
1500     nodes = getNodesFromXPath (node->doc, node, "value", &num);
1501     if (num)
1502     {
1503 	for (j = 0; j < num; j++)
1504 	{
1505 	    void *valuePBv = NULL;
1506 #ifdef USE_PROTOBUF
1507 	    if (optionPBv)
1508 		valuePBv = ((OptionMetadata *) optionPBv)->add_default_value ();
1509 #endif
1510 	    CCSSettingValue *val;
1511 	    val = (CCSSettingValue *) calloc (1, sizeof (CCSSettingValue));
1512 	    if (!val)
1513 		continue;
1514 
1515 	    val->parent = v->parent;
1516 	    val->isListChild = TRUE;
1517 
1518 	    switch (i->forList.listType)
1519 	    {
1520 	    case TypeBool:
1521 		initBoolValue (val, nodes[j], valuePBv);
1522 		break;
1523 	    case TypeInt:
1524 		initIntValue (val, i->forList.listInfo, nodes[j], valuePBv);
1525 		break;
1526 	    case TypeFloat:
1527 		initFloatValue (val, i->forList.listInfo, nodes[j], valuePBv);
1528 		break;
1529 	    case TypeString:
1530 		initStringValue (val, i->forList.listInfo, nodes[j], valuePBv);
1531 		break;
1532 	    case TypeColor:
1533 		initColorValue (val, nodes[j], valuePBv);
1534 		break;
1535 	    case TypeKey:
1536 		initKeyValue (val, i->forList.listInfo, nodes[j], valuePBv);
1537 		break;
1538 	    case TypeButton:
1539 		initButtonValue (val, i->forList.listInfo, nodes[j], valuePBv);
1540 		break;
1541 	    case TypeEdge:
1542 		initEdgeValue (val, i->forList.listInfo, nodes[j], valuePBv);
1543 		break;
1544 	    case TypeBell:
1545 		initBellValue (val, i->forList.listInfo, nodes[j], valuePBv);
1546 		break;
1547 	    case TypeMatch:
1548 		initMatchValue (val, nodes[j], valuePBv);
1549 	    default:
1550 		break;
1551 	    }
1552 	    v->value.asList = ccsSettingValueListAppend (v->value.asList, val);
1553 	}
1554 	free (nodes);
1555     }
1556 }
1557 
1558 static void
initIntInfo(CCSSettingInfo * i,xmlNode * node,void * optionPBv)1559 initIntInfo (CCSSettingInfo * i, xmlNode * node, void * optionPBv)
1560 {
1561     xmlNode **nodes;
1562     char *name;
1563     char *value;
1564     int num, j;
1565     i->forInt.min = MINSHORT;
1566     i->forInt.max = MAXSHORT;
1567     i->forInt.desc = NULL;
1568 
1569     value = getStringFromXPath (node->doc, node, "min/child::text()");
1570     if (value)
1571     {
1572 	int val = strtol (value, NULL, 0);
1573 	i->forInt.min = val;
1574 	free (value);
1575 #ifdef USE_PROTOBUF
1576 	if (optionPBv)
1577 	    ((OptionMetadata *) optionPBv)->set_int_min (val);
1578 #endif
1579     }
1580 
1581     value = getStringFromXPath (node->doc, node, "max/child::text()");
1582     if (value)
1583     {
1584 	int val = strtol (value, NULL, 0);
1585 	i->forInt.max = val;
1586 	free (value);
1587 #ifdef USE_PROTOBUF
1588 	if (optionPBv)
1589 	    ((OptionMetadata *) optionPBv)->set_int_max (val);
1590 #endif
1591     }
1592 
1593     if (!basicMetadata)
1594     {
1595 	nodes = getNodesFromXPath (node->doc, node, "desc", &num);
1596 	if (num)
1597 	{
1598 	    for (j = 0; j < num; j++)
1599 	    {
1600 		value = getStringFromXPath (node->doc, nodes[j],
1601 					    "value/child::text()");
1602 		if (value)
1603 		{
1604 		    int val = strtol (value, NULL, 0);
1605 		    free (value);
1606 
1607 		    if (val >= i->forInt.min && val <= i->forInt.max)
1608 		    {
1609 			name = stringFromNodeDefTrans (nodes[j],
1610 						       "name/child::text()",
1611 						       NULL);
1612 			if (name)
1613 			{
1614 			    CCSIntDesc *intDesc;
1615 
1616 			    intDesc = (CCSIntDesc *) calloc (1, sizeof (CCSIntDesc));
1617 			    if (intDesc)
1618 			    {
1619 				intDesc->name = strdup (name);
1620 				intDesc->value = val;
1621 				i->forInt.desc =
1622 				    ccsIntDescListAppend (i->forInt.desc,
1623 							  intDesc);
1624 #ifdef USE_PROTOBUF
1625 				if (optionPBv)
1626 				{
1627 				    OptionMetadata::IntDescription *intDescPB =
1628 					((OptionMetadata *) optionPBv)->
1629 					add_int_desc ();
1630 				    intDescPB->set_value (val);
1631 				    intDescPB->set_name (name);
1632 				}
1633 #endif
1634 			    }
1635 			    free (name);
1636 			}
1637 		    }
1638 		}
1639 	    }
1640 	    free (nodes);
1641 	}
1642     }
1643 }
1644 
1645 static void
initFloatInfo(CCSSettingInfo * i,xmlNode * node,void * optionPBv)1646 initFloatInfo (CCSSettingInfo * i, xmlNode * node, void * optionPBv)
1647 {
1648     char *value;
1649     char *loc;
1650 
1651     i->forFloat.min = MINSHORT;
1652     i->forFloat.max = MAXSHORT;
1653     i->forFloat.precision = 0.1f;
1654 
1655     loc = setlocale (LC_NUMERIC, NULL);
1656     setlocale (LC_NUMERIC, "C");
1657     value = getStringFromXPath (node->doc, node, "min/child::text()");
1658     if (value)
1659     {
1660 	float val = strtod (value, NULL);
1661 	i->forFloat.min = val;
1662 	free (value);
1663 #ifdef USE_PROTOBUF
1664 	if (optionPBv)
1665 	    ((OptionMetadata *) optionPBv)->set_float_min (val);
1666 #endif
1667     }
1668 
1669     value = getStringFromXPath (node->doc, node, "max/child::text()");
1670     if (value)
1671     {
1672 	float val = strtod (value, NULL);
1673 	i->forFloat.max = val;
1674 	free (value);
1675 #ifdef USE_PROTOBUF
1676 	if (optionPBv)
1677 	    ((OptionMetadata *) optionPBv)->set_float_max (val);
1678 #endif
1679     }
1680 
1681     value = getStringFromXPath (node->doc, node, "precision/child::text()");
1682     if (value)
1683     {
1684 	float val = strtod (value, NULL);
1685 	i->forFloat.precision = val;
1686 	free (value);
1687 #ifdef USE_PROTOBUF
1688 	if (optionPBv)
1689 	    ((OptionMetadata *) optionPBv)->set_precision (val);
1690 #endif
1691     }
1692 
1693     setlocale (LC_NUMERIC, loc);
1694 }
1695 
1696 static void
initStringInfo(CCSSettingInfo * i,xmlNode * node,void * optionPBv)1697 initStringInfo (CCSSettingInfo * i, xmlNode * node, void * optionPBv)
1698 {
1699     xmlNode **nodes;
1700     char *name;
1701     char *value;
1702     int num, j;
1703     i->forString.restriction = NULL;
1704     i->forString.sortStartsAt = -1;
1705     i->forString.extensible = FALSE;
1706 
1707     if (!basicMetadata)
1708     {
1709 	if (nodeExists (node, "extensible"))
1710 	{
1711 	    i->forString.extensible = TRUE;
1712 #ifdef USE_PROTOBUF
1713 	    if (optionPBv)
1714 		((OptionMetadata *) optionPBv)->set_extensible (TRUE);
1715 #endif
1716 	}
1717 
1718 	nodes = getNodesFromXPath (node->doc, node, "sort", &num);
1719 	if (num)
1720 	{
1721 	    int val = 0; /* Start sorting at 0 unless otherwise specified. */
1722 
1723 	    value = getStringFromXPath (node->doc, nodes[0], "@start");
1724 	    if (value)
1725 	    {
1726 		/* Custom starting value specified. */
1727 		val = strtol (value, NULL, 0);
1728 		if (val < 0)
1729 		    val = 0;
1730 		free (value);
1731 	    }
1732 	    i->forString.sortStartsAt = val;
1733 #ifdef USE_PROTOBUF
1734 	    if (optionPBv)
1735 		((OptionMetadata *) optionPBv)->set_sort_start (val);
1736 #endif
1737 	    free (nodes);
1738 	}
1739 
1740 	nodes = getNodesFromXPath (node->doc, node, "restriction", &num);
1741 	if (num)
1742 	{
1743 	    for (j = 0; j < num; j++)
1744 	    {
1745 #ifdef USE_PROTOBUF
1746 		OptionMetadata::StringRestriction * strRestrictionPB = NULL;
1747 		if (optionPBv)
1748 		    strRestrictionPB =
1749 			((OptionMetadata *) optionPBv)->add_str_restriction ();
1750 #endif
1751 		value = getStringFromXPath (node->doc, nodes[j],
1752 					   "value/child::text()");
1753 		if (value)
1754 		{
1755 		    name = stringFromNodeDefTrans (nodes[j],
1756 						   "name/child::text()",
1757 						   NULL);
1758 		    if (name)
1759 		    {
1760 			ccsAddRestrictionToStringInfo (&i->forString,
1761 						       name, value);
1762 #ifdef USE_PROTOBUF
1763 			if (strRestrictionPB)
1764 			{
1765 			    strRestrictionPB->set_value (value);
1766 			    strRestrictionPB->set_name (name);
1767 			}
1768 #endif
1769 			free (name);
1770 		    }
1771 		    free (value);
1772 		}
1773 	    }
1774 	    free (nodes);
1775 	}
1776     }
1777 }
1778 
1779 static void
initListInfo(CCSSettingInfo * i,xmlNode * node,void * optionPBv)1780 initListInfo (CCSSettingInfo * i, xmlNode * node, void * optionPBv)
1781 {
1782     char *value;
1783     CCSSettingInfo *info;
1784 
1785     i->forList.listType = TypeBool;
1786     i->forList.listInfo = NULL;
1787 
1788     value = getStringFromXPath (node->doc, node, "type/child::text()");
1789 
1790     if (!value)
1791 	return;
1792 
1793     i->forList.listType = getOptionType (value);
1794 #ifdef USE_PROTOBUF
1795     if (optionPBv)
1796 	((OptionMetadata *) optionPBv)->set_list_type
1797 	    ((OptionMetadata::Type) i->forList.listType);
1798 #endif
1799 
1800     free (value);
1801 
1802     switch (i->forList.listType)
1803     {
1804     case TypeInt:
1805 	{
1806 	    info = (CCSSettingInfo *) calloc (1, sizeof (CCSSettingInfo));
1807 	    if (info)
1808 		initIntInfo (info, node, optionPBv);
1809 	    i->forList.listInfo = info;
1810 	}
1811 	break;
1812     case TypeFloat:
1813 	{
1814 	    info = (CCSSettingInfo *) calloc (1, sizeof (CCSSettingInfo));
1815 	    if (info)
1816 		initFloatInfo (info, node, optionPBv);
1817 	    i->forList.listInfo = info;
1818 	}
1819 	break;
1820     case TypeString:
1821 	{
1822 	    info = (CCSSettingInfo *) calloc (1, sizeof (CCSSettingInfo));
1823 	    if (info)
1824 		initStringInfo (info, node, optionPBv);
1825 	    i->forList.listInfo = info;
1826 	}
1827 	break;
1828     default:
1829 	break;
1830     }
1831 }
1832 
1833 static void
initActionInfo(CCSSettingInfo * i,xmlNode * node,void * optionPBv)1834 initActionInfo (CCSSettingInfo * i, xmlNode * node, void * optionPBv)
1835 {
1836     char *value;
1837 
1838     i->forAction.internal = FALSE;
1839 
1840     value = getStringFromXPath (node->doc, node, "internal/child::text()");
1841     if (value)
1842     {
1843 	if (strcasecmp (value, "true") == 0)
1844 	{
1845 	    i->forAction.internal = TRUE;
1846 #ifdef USE_PROTOBUF
1847 	    if (optionPBv)
1848 		((OptionMetadata *) optionPBv)->set_internal (TRUE);
1849 #endif
1850 	}
1851 	free (value);
1852 	return;
1853     }
1854     if (nodeExists (node, "internal"))
1855     {
1856 	i->forAction.internal = TRUE;
1857 #ifdef USE_PROTOBUF
1858 	if (optionPBv)
1859 	    ((OptionMetadata *) optionPBv)->set_internal (TRUE);
1860 #endif
1861     }
1862 }
1863 
1864 #ifdef USE_PROTOBUF
1865 static void
checkAddGroupSubgroup(OptionMetadata * optPB,StringList * descList,char * name,Bool isGroup)1866 checkAddGroupSubgroup (OptionMetadata *optPB,
1867 		       StringList *descList,
1868 		       char *name,
1869 		       Bool isGroup)
1870 {
1871     // Check if group has the same name as the last group in the groups list
1872     int len = descList->size ();
1873     if (len > 0 &&
1874 	strcmp (name, descList->Get (len - 1).c_str ()) == 0)
1875     {
1876 	if (isGroup)
1877 	    optPB->set_group_id (len - 1);
1878 	else
1879 	    optPB->set_subgroup_id (len - 1);
1880     }
1881     else
1882     {
1883 	// Add new group to the list
1884         descList->Add ()->assign (name);
1885 
1886 	if (isGroup)
1887 	    optPB->set_group_id (len);
1888 	else
1889 	    optPB->set_subgroup_id (len);
1890     }
1891 }
1892 
1893 static Bool
createProtoBufCacheDir()1894 createProtoBufCacheDir ()
1895 {
1896     if (metadataCacheDir.length () > 0)
1897     {
1898 	// Cache dir must have been created already, since otherwise it would
1899 	// be "". So we can return here.
1900 	return TRUE;
1901     }
1902     char *cacheBaseDir = NULL;
1903     char *cacheHome = getenv ("XDG_CACHE_HOME");
1904 
1905     if (cacheHome && strlen (cacheHome))
1906     {
1907 	asprintf (&cacheBaseDir, "%s", cacheHome);
1908     }
1909     else
1910     {
1911 	char *home = getenv ("HOME");
1912 	if (home && strlen (home))
1913 	{
1914 	    asprintf (&cacheBaseDir, "%s/.cache", home);
1915 	}
1916     }
1917 
1918     if (cacheBaseDir)
1919     {
1920 	metadataCacheDir = cacheBaseDir;
1921 	if (metadataCacheDir[metadataCacheDir.length () - 1] != '/')
1922 	    metadataCacheDir += "/";
1923 	metadataCacheDir += "compizconfig";
1924 	std::string metadataCacheFileDummy = metadataCacheDir + "/dummy";
1925 
1926 	// Create cache dir
1927 	Bool success = ccsCreateDirFor (metadataCacheFileDummy.c_str ());
1928 	if (!success)
1929 	    fprintf (stderr, "[ERROR]: Error creating directory \"%s\"\n",
1930 		     metadataCacheDir.c_str ());
1931 	free (cacheBaseDir);
1932 
1933 	if (success)
1934 	    return TRUE; // metadataCacheDir will be used later in this case
1935 
1936 	metadataCacheDir = ""; // invalidate metadataCacheDir
1937     }
1938 
1939     usingProtobuf = FALSE; // Disable protobuf if cache dir cannot be created
1940     return FALSE;
1941 }
1942 
1943 #endif
1944 
1945 static void
addOptionForPlugin(CCSPlugin * plugin,char * name,char * type,Bool isReadonly,Bool isScreen,unsigned int screen,xmlNode * node,void * groupListPBv,void * subgroupListPBv,void * optionPBv)1946 addOptionForPlugin (CCSPlugin * plugin,
1947 		    char * name,
1948 		    char * type,
1949 		    Bool isReadonly,
1950 		    Bool isScreen,
1951 		    unsigned int screen,
1952 		    xmlNode * node,
1953 		    void * groupListPBv,
1954 		    void * subgroupListPBv,
1955 		    void * optionPBv)
1956 {
1957     xmlNode **nodes;
1958     int num = 0;
1959     CCSSetting *setting;
1960 
1961     if (ccsFindSetting (plugin, name, isScreen, screen))
1962     {
1963 	fprintf (stderr, "[ERROR]: Option \"%s\" already defined\n", name);
1964 	return;
1965     }
1966 
1967     if (getOptionType (type) == TypeNum)
1968 	return;
1969 
1970     setting = (CCSSetting *) calloc (1, sizeof (CCSSetting));
1971     if (!setting)
1972 	return;
1973 
1974     setting->parent = plugin;
1975     setting->isScreen = isScreen;
1976     setting->screenNum = screen;
1977     setting->isDefault = TRUE;
1978     setting->name = strdup (name);
1979 
1980     if (!basicMetadata)
1981     {
1982 	setting->shortDesc =
1983 	    stringFromNodeDefTrans (node, "short/child::text()", name);
1984 	setting->longDesc =
1985 	    stringFromNodeDefTrans (node, "long/child::text()", "");
1986 	setting->hints = stringFromNodeDef (node, "hints/child::text()", "");
1987 	setting->group =
1988 	    stringFromNodeDefTrans (node, "ancestor::group/short/child::text()",
1989 				    "");
1990 	setting->subGroup =
1991 	    stringFromNodeDefTrans (node,
1992 				    "ancestor::subgroup/short/child::text()",
1993 				    "");
1994     }
1995     else
1996     {
1997 	setting->shortDesc = strdup (name);
1998 	setting->longDesc  = strdup ("");
1999 	setting->hints     = strdup ("");
2000 	setting->group     = strdup ("");
2001 	setting->subGroup  = strdup ("");
2002     }
2003     setting->type = getOptionType (type);
2004 
2005 #ifdef USE_PROTOBUF
2006     OptionMetadata *optPB = NULL;
2007 
2008     if (optionPBv)
2009     {
2010 	optPB = (OptionMetadata *) optionPBv;
2011 
2012 	optPB->set_name (name);
2013 	optPB->set_type ((OptionMetadata::Type) setting->type);
2014 	if (isReadonly)
2015 	    optPB->set_read_only (isReadonly);
2016 
2017 	optPB->set_short_desc (setting->shortDesc);
2018 	optPB->set_long_desc (setting->longDesc);
2019 
2020 	if (strlen (setting->hints) > 0)
2021 	    optPB->set_hints (setting->hints);
2022 
2023 	if (groupListPBv && strlen (setting->group) > 0)
2024 	    checkAddGroupSubgroup (optPB, (StringList *) groupListPBv,
2025 				   setting->group, TRUE);
2026 	if (subgroupListPBv && strlen (setting->subGroup) > 0)
2027 	    checkAddGroupSubgroup (optPB, (StringList *) subgroupListPBv,
2028 				   setting->subGroup, FALSE);
2029     }
2030 #endif
2031     setting->value = &setting->defaultValue;
2032     setting->defaultValue.parent = setting;
2033 
2034     switch (setting->type)
2035     {
2036     case TypeInt:
2037 	initIntInfo (&setting->info, node, optionPBv);
2038 	break;
2039     case TypeFloat:
2040 	initFloatInfo (&setting->info, node, optionPBv);
2041 	break;
2042     case TypeString:
2043 	initStringInfo (&setting->info, node, optionPBv);
2044 	break;
2045     case TypeList:
2046 	initListInfo (&setting->info, node, optionPBv);
2047 	break;
2048     case TypeKey:
2049     case TypeButton:
2050     case TypeEdge:
2051     case TypeBell:
2052 	initActionInfo (&setting->info, node, optionPBv);
2053 	break;
2054     default:
2055 	break;
2056     }
2057 
2058     nodes = getNodesFromXPath (node->doc, node, "default", &num);
2059     if (num)
2060     {
2061 	void * valuePBv = NULL;
2062 #ifdef USE_PROTOBUF
2063 	if (optPB && setting->type != TypeList)
2064 	    valuePBv = optPB->add_default_value ();
2065 #endif
2066 	switch (setting->type)
2067 	{
2068 	case TypeInt:
2069 	    initIntValue (&setting->defaultValue, &setting->info, nodes[0],
2070 			  valuePBv);
2071 	    break;
2072 	case TypeBool:
2073 	    initBoolValue (&setting->defaultValue, nodes[0],
2074 			   valuePBv);
2075 	    break;
2076 	case TypeFloat:
2077 	    initFloatValue (&setting->defaultValue, &setting->info, nodes[0],
2078 			    valuePBv);
2079 	    break;
2080 	case TypeString:
2081 	    initStringValue (&setting->defaultValue, &setting->info, nodes[0],
2082 			     valuePBv);
2083 	    break;
2084 	case TypeColor:
2085 	    initColorValue (&setting->defaultValue, nodes[0], valuePBv);
2086 	    break;
2087 	case TypeKey:
2088 	    initKeyValue (&setting->defaultValue, &setting->info, nodes[0],
2089 			  valuePBv);
2090 	    break;
2091 	case TypeButton:
2092 	    initButtonValue (&setting->defaultValue, &setting->info, nodes[0],
2093 			     valuePBv);
2094 	    break;
2095 	case TypeEdge:
2096 	    initEdgeValue (&setting->defaultValue, &setting->info, nodes[0],
2097 			   valuePBv);
2098 	    break;
2099 	case TypeBell:
2100 	    initBellValue (&setting->defaultValue, &setting->info, nodes[0],
2101 			   valuePBv);
2102 	    break;
2103 	case TypeMatch:
2104 	    initMatchValue (&setting->defaultValue, nodes[0],
2105 			    valuePBv);
2106 	    break;
2107 	case TypeList:
2108 	    initListValue (&setting->defaultValue, &setting->info, nodes[0],
2109 			   optionPBv);
2110 	    break;
2111 	default:
2112 	    break;
2113 	}
2114     }
2115     else
2116     {
2117 	/* if we have no set defaults, we have at least to set
2118 	   the string defaults to empty strings */
2119 	switch (setting->type)
2120 	{
2121 	case TypeString:
2122 	    setting->defaultValue.value.asString = strdup ("");
2123 	    break;
2124 	case TypeMatch:
2125 	    setting->defaultValue.value.asMatch = strdup ("");
2126 	    break;
2127 	default:
2128 	    break;
2129 	}
2130     }
2131 
2132     if (nodes)
2133 	free (nodes);
2134 
2135     if (isReadonly)
2136     {
2137 	// Will come here only when protobuf is enabled
2138 	ccsFreeSetting (setting);
2139 	return;
2140     }
2141     //	printSetting (setting);
2142     PLUGIN_PRIV (plugin);
2143     pPrivate->settings = ccsSettingListAppend (pPrivate->settings, setting);
2144 }
2145 
2146 static void
addOptionFromXMLNode(CCSPlugin * plugin,xmlNode * node,Bool isScreen,void * groupListPBv,void * subgroupListPBv,void * optionPBv)2147 addOptionFromXMLNode (CCSPlugin * plugin,
2148 		      xmlNode * node,
2149 		      Bool isScreen,
2150 		      void * groupListPBv,
2151 		      void * subgroupListPBv,
2152 		      void * optionPBv)
2153 {
2154     char *name;
2155     char *type;
2156     char *readonly;
2157     Bool isReadonly;
2158 
2159     if (!node)
2160 	return;
2161 
2162     name = getStringFromXPath (node->doc, node, "@name");
2163 
2164     type = getStringFromXPath (node->doc, node, "@type");
2165 
2166     readonly = getStringFromXPath (node->doc, node, "@read_only");
2167     isReadonly = readonly && !strcmp (readonly, "true");
2168 
2169     // If optionPBv is non-NULL, we still want to get the option info to write
2170     // to .pb file, so we don't return immediately in that case.
2171 
2172     if (!name || !strlen (name) || !type || !strlen (type) ||
2173 	(!optionPBv && isReadonly))
2174     {
2175 	if (name)
2176 	    free (name);
2177 	if (type)
2178 	    free (type);
2179 	if (readonly)
2180 	    free (readonly);
2181 
2182 	return;
2183     }
2184 
2185     if (isScreen)
2186     {
2187 	for (unsigned i = 0; i < plugin->context->numScreens; i++)
2188 	    addOptionForPlugin (plugin, name, type, isReadonly, TRUE,
2189 				plugin->context->screens[i], node,
2190 				groupListPBv, subgroupListPBv, optionPBv);
2191     }
2192     else
2193     {
2194 	addOptionForPlugin (plugin, name, type, isReadonly, FALSE, 0, node,
2195 			    groupListPBv, subgroupListPBv, optionPBv);
2196     }
2197     free (name);
2198     free (type);
2199 
2200     if (readonly)
2201 	free (readonly);
2202 }
2203 
2204 static void
initDisplayScreenFromRootNode(CCSPlugin * plugin,xmlNode * node,Bool isScreen,void * pluginPBv)2205 initDisplayScreenFromRootNode (CCSPlugin * plugin,
2206 			       xmlNode * node,
2207 			       Bool isScreen,
2208 			       void * pluginPBv)
2209 {
2210     xmlNode **nodes;
2211     xmlNode **optNodes;
2212     int num, i;
2213     void *groupListPBv = NULL;
2214     void *subgroupListPBv = NULL;
2215 
2216     nodes = getNodesFromXPath (node->doc, node,
2217 			       (isScreen ? "screen" : "display"), &num);
2218     if (!num)
2219 	return;
2220 
2221 #ifdef USE_PROTOBUF
2222     ScreenMetadata *screenPB = NULL;
2223 
2224     if (pluginPBv)
2225     {
2226 	PluginMetadata *pluginPB = (PluginMetadata *) pluginPBv;
2227 	screenPB = (isScreen ?
2228 		    pluginPB->mutable_screen () :
2229 		    pluginPB->mutable_display ());
2230 	groupListPBv = screenPB->mutable_group_desc ();
2231 	subgroupListPBv = screenPB->mutable_subgroup_desc ();
2232     }
2233 #endif
2234     optNodes = getNodesFromXPath
2235 	(node->doc, nodes[0],
2236 	 "option | group/subgroup/option | group/option | subgroup/option",
2237 	 &num);
2238     if (num)
2239     {
2240 	for (i = 0; i < num; i++)
2241 	{
2242 	    void *optionPBv = NULL;
2243     #ifdef USE_PROTOBUF
2244 	    if (screenPB)
2245 		optionPBv = screenPB->add_option ();
2246     #endif
2247 	    addOptionFromXMLNode (plugin, optNodes[i], isScreen,
2248 				  groupListPBv, subgroupListPBv, optionPBv);
2249 	}
2250 
2251 	free (optNodes);
2252     }
2253     free (nodes);
2254 }
2255 
2256 static inline void
initOptionsFromRootNode(CCSPlugin * plugin,xmlNode * node,void * pluginPBv)2257 initOptionsFromRootNode (CCSPlugin * plugin,
2258 			 xmlNode * node,
2259 			 void * pluginPBv)
2260 {
2261     // For display
2262     initDisplayScreenFromRootNode (plugin, node, FALSE, pluginPBv);
2263 
2264     // For screen
2265     initDisplayScreenFromRootNode (plugin, node, TRUE, pluginPBv);
2266 }
2267 
2268 static void
addStringsFromPath(CCSStringList * list,const char * path,xmlNode * node,void * stringListPBv)2269 addStringsFromPath (CCSStringList * list,
2270 		    const char * path,
2271 		    xmlNode * node,
2272 		    void * stringListPBv)
2273 {
2274     xmlNode **nodes;
2275     int num, i;
2276     nodes = getNodesFromXPath (node->doc, node, path, &num);
2277 
2278     if (num)
2279     {
2280 	for (i = 0; i < num; i++)
2281 	{
2282 	    char *value = stringFromNodeDef (nodes[i], "child::text()", NULL);
2283 
2284 	    if (value && strlen (value))
2285 	    {
2286 		*list = ccsStringListAppend (*list, value);
2287 #ifdef USE_PROTOBUF
2288 		if (stringListPBv)
2289 		    ((StringList *) stringListPBv)->Add ()->assign (value);
2290 #endif
2291 	    }
2292 	    if (value && !strlen (value))
2293 		free (value);
2294 	}
2295 
2296 	free (nodes);
2297     }
2298 }
2299 
2300 static void
addStringExtensionFromXMLNode(CCSPlugin * plugin,xmlNode * node,void * extensionPBv)2301 addStringExtensionFromXMLNode (CCSPlugin * plugin,
2302 			       xmlNode * node,
2303 			       void * extensionPBv)
2304 {
2305     xmlNode **nodes;
2306     int num, j;
2307     CCSStrExtension *extension;
2308     char *name;
2309     char *value;
2310     char *isDisplay;
2311     void * stringListPBv = NULL;
2312 
2313     extension = (CCSStrExtension *) calloc (1, sizeof (CCSStrExtension));
2314     if (!extension)
2315 	return;
2316 
2317     isDisplay = getStringFromXPath (node->doc, node, "@display");
2318 
2319     extension->isScreen = !(isDisplay && !strcmp (isDisplay, "true"));
2320 
2321     if (isDisplay)
2322 	free (isDisplay);
2323 
2324     extension->restriction = NULL;
2325 
2326     extension->basePlugin = getStringFromXPath (node->doc, node, "@base_plugin");
2327     if (!extension->basePlugin)
2328 	extension->basePlugin = strdup ("");
2329 
2330 #ifdef USE_PROTOBUF
2331     ExtensionMetadata * extensionPB = NULL;
2332     if (extensionPBv)
2333     {
2334 	extensionPB = (ExtensionMetadata *) extensionPBv;
2335 	extensionPB->set_display (!extension->isScreen);
2336 	extensionPB->set_base_plugin (extension->basePlugin);
2337 	stringListPBv = extensionPB->mutable_base_option ();
2338     }
2339 #endif
2340 
2341     addStringsFromPath (&extension->baseSettings, "base_option", node,
2342 			stringListPBv);
2343 
2344     nodes = getNodesFromXPath (node->doc, node, "restriction", &num);
2345     if (!num)
2346     {
2347 	free (extension);
2348 	return;
2349     }
2350 
2351     for (j = 0; j < num; j++)
2352     {
2353 	value = getStringFromXPath (node->doc, nodes[j], "value/child::text()");
2354 	if (value)
2355 	{
2356 	    name = stringFromNodeDefTrans (nodes[j], "name/child::text()",
2357 					   NULL);
2358 	    if (name)
2359 	    {
2360 		ccsAddRestrictionToStringExtension (extension, name, value);
2361 #ifdef USE_PROTOBUF
2362 		if (extensionPB)
2363 		{
2364 		    OptionMetadata::StringRestriction *strRestrictionPB =
2365 			extensionPB->add_str_restriction ();
2366 		    strRestrictionPB->set_value (value);
2367 		    strRestrictionPB->set_name (name);
2368 		}
2369 #endif
2370 		free (name);
2371 	    }
2372 	    free (value);
2373 	}
2374     }
2375     free (nodes);
2376 
2377     PLUGIN_PRIV (plugin);
2378 
2379     pPrivate->stringExtensions =
2380 	ccsStrExtensionListAppend (pPrivate->stringExtensions, extension);
2381 }
2382 
2383 static void
initStringExtensionsFromRootNode(CCSPlugin * plugin,xmlNode * node,void * pluginPBv)2384 initStringExtensionsFromRootNode (CCSPlugin * plugin,
2385 				  xmlNode * node,
2386 				  void * pluginPBv)
2387 {
2388     xmlNode **nodes;
2389     int num, i;
2390     nodes = getNodesFromXPath (node->doc, node, "/compiz/*/extension", &num);
2391 
2392     for (i = 0; i < num; i++)
2393     {
2394 	void *extensionPBv = NULL;
2395 #ifdef USE_PROTOBUF
2396 	if (pluginPBv)
2397 	{
2398 	    PluginMetadata *pluginPB = (PluginMetadata *) pluginPBv;
2399 	    extensionPBv = pluginPB->add_extension ();
2400 	}
2401 #endif
2402 	addStringExtensionFromXMLNode (plugin, nodes[i], extensionPBv);
2403     }
2404     free (nodes);
2405 }
2406 
2407 static void
initRulesFromRootNode(CCSPlugin * plugin,xmlNode * node,void * pluginInfoPBv)2408 initRulesFromRootNode (CCSPlugin * plugin, xmlNode * node, void * pluginInfoPBv)
2409 {
2410     void *featureListPBv = NULL;
2411     void *pluginAfterListPBv = NULL;
2412     void *pluginBeforeListPBv = NULL;
2413     void *requirePluginListPBv = NULL;
2414     void *requireFeatureListPBv = NULL;
2415     void *conflictPluginListPBv = NULL;
2416     void *conflictFeatureListPBv = NULL;
2417 #ifdef USE_PROTOBUF
2418     if (pluginInfoPBv)
2419     {
2420 	PluginInfoMetadata *pluginInfoPB = (PluginInfoMetadata *) pluginInfoPBv;
2421 	featureListPBv = pluginInfoPB->mutable_feature ();
2422 
2423 	DependenciesMetadata *deps = pluginInfoPB->mutable_deps ();
2424 	pluginAfterListPBv     = deps->mutable_after_plugin ();
2425 	pluginBeforeListPBv    = deps->mutable_before_plugin ();
2426 	requirePluginListPBv   = deps->mutable_require_plugin ();
2427 	requireFeatureListPBv  = deps->mutable_require_feature ();
2428 	conflictPluginListPBv  = deps->mutable_conflict_plugin ();
2429 	conflictFeatureListPBv = deps->mutable_conflict_feature ();
2430     }
2431 #endif
2432 
2433     addStringsFromPath (&plugin->providesFeature, "feature", node,
2434 			featureListPBv);
2435 
2436     addStringsFromPath (&plugin->loadAfter,
2437 			"deps/relation[@type = 'after']/plugin", node,
2438 			pluginAfterListPBv);
2439     addStringsFromPath (&plugin->loadBefore,
2440 			"deps/relation[@type = 'before']/plugin", node,
2441 			pluginBeforeListPBv);
2442     addStringsFromPath (&plugin->requiresPlugin,
2443 			"deps/requirement/plugin", node, requirePluginListPBv);
2444     addStringsFromPath (&plugin->requiresFeature,
2445 			"deps/requirement/feature", node, requireFeatureListPBv);
2446     addStringsFromPath (&plugin->conflictPlugin,
2447 			"deps/conflict/plugin", node, conflictPluginListPBv);
2448     addStringsFromPath (&plugin->conflictFeature,
2449 			"deps/conflict/feature", node, conflictFeatureListPBv);
2450 }
2451 
2452 #ifdef USE_PROTOBUF
2453 static void
fillBasicInfoIntoPB(CCSPlugin * plugin,PluginInfoMetadata * pluginInfoPB)2454 fillBasicInfoIntoPB (CCSPlugin *plugin, PluginInfoMetadata *pluginInfoPB)
2455 {
2456     if (!pluginInfoPB)
2457 	return;
2458 
2459     pluginInfoPB->set_name (plugin->name);
2460     pluginInfoPB->set_short_desc (plugin->shortDesc);
2461     pluginInfoPB->set_long_desc (plugin->longDesc);
2462     pluginInfoPB->set_category (plugin->category);
2463 }
2464 #endif
2465 
2466 /* Returns TRUE on success. */
2467 static Bool
addPluginFromXMLNode(CCSContext * context,xmlNode * node,char * file,void * pluginInfoPBv)2468 addPluginFromXMLNode (CCSContext * context,
2469 		      xmlNode * node,
2470 		      char * file,
2471 		      void * pluginInfoPBv)
2472 {
2473     char *name;
2474     CCSPlugin *plugin;
2475     CCSPluginPrivate *pPrivate;
2476 
2477     if (!node)
2478 	return FALSE;
2479 
2480     name = getStringFromXPath (node->doc, node, "@name");
2481 
2482     if (!name || !strlen (name))
2483     {
2484 	if (name)
2485 	    free (name);
2486 	return FALSE;
2487     }
2488 
2489     if (!strcmp (name, "ini") || !strcmp (name, "gconf") ||
2490 	!strcmp (name, "ccp") || !strcmp (name, "kconfig"))
2491     {
2492 	free (name);
2493 	return FALSE;
2494     }
2495 
2496     if (ccsFindPlugin (context, name))
2497     {
2498 	free (name);
2499 	return FALSE;
2500     }
2501 
2502     plugin = (CCSPlugin *) calloc (1, sizeof (CCSPlugin));
2503     if (!plugin)
2504 	return FALSE;
2505 
2506     pPrivate = (CCSPluginPrivate *) calloc (1, sizeof (CCSPluginPrivate));
2507     if (!pPrivate)
2508     {
2509 	free (plugin);
2510 	return FALSE;
2511     }
2512 
2513     plugin->ccsPrivate = (void *) pPrivate;
2514 
2515     if (file)
2516 	pPrivate->xmlFile = strdup (file);
2517 
2518     asprintf (&pPrivate->xmlPath, "/compiz/plugin[@name = '%s']", name);
2519     plugin->context = context;
2520     plugin->name = strdup (name);
2521 
2522     if (!basicMetadata)
2523     {
2524 	plugin->shortDesc =
2525 	    stringFromNodeDefTrans (node, "short/child::text()", name);
2526 	plugin->longDesc =
2527 	    stringFromNodeDefTrans (node, "long/child::text()",	name);
2528 	plugin->category =
2529 	    stringFromNodeDef (node, "category/child::text()", "");
2530     }
2531     else
2532     {
2533 	plugin->shortDesc = strdup (name);
2534 	plugin->longDesc  = strdup (name);
2535 	plugin->category  = strdup ("");
2536     }
2537 #ifdef USE_PROTOBUF
2538     fillBasicInfoIntoPB (plugin, (PluginInfoMetadata *) pluginInfoPBv);
2539 #endif
2540 
2541     initRulesFromRootNode (plugin, node, pluginInfoPBv);
2542 
2543     context->plugins = ccsPluginListAppend (context->plugins, plugin);
2544     free (name);
2545 
2546     return TRUE;
2547 }
2548 
2549 /* Returns TRUE on success. */
2550 static Bool
addCoreSettingsFromXMLNode(CCSContext * context,xmlNode * node,char * file,void * pluginInfoPBv)2551 addCoreSettingsFromXMLNode (CCSContext * context,
2552 			    xmlNode * node,
2553 			    char *file,
2554 			    void * pluginInfoPBv)
2555 {
2556     CCSPlugin *plugin;
2557     CCSPluginPrivate *pPrivate;
2558 
2559     if (!node)
2560 	return FALSE;
2561 
2562     if (ccsFindPlugin (context, "core"))
2563 	return FALSE;
2564 
2565     plugin = (CCSPlugin *) calloc (1, sizeof (CCSPlugin));
2566     if (!plugin)
2567 	return FALSE;
2568 
2569     pPrivate = (CCSPluginPrivate *) calloc (1, sizeof (CCSPluginPrivate));
2570     if (!pPrivate)
2571     {
2572 	free (plugin);
2573 	return FALSE;
2574     }
2575 
2576     plugin->ccsPrivate = (void *) pPrivate;
2577 
2578     if (file)
2579 	pPrivate->xmlFile = strdup (file);
2580 
2581     pPrivate->xmlPath = strdup ("/compiz/core");
2582     plugin->context = context;
2583     plugin->name = strdup ("core");
2584     plugin->category = strdup ("General");
2585 
2586     if (!basicMetadata)
2587     {
2588 	plugin->shortDesc =
2589 	    stringFromNodeDefTrans (node, "short/child::text()",
2590 				    "General Options");
2591 	plugin->longDesc =
2592 	    stringFromNodeDefTrans (node, "long/child::text()",
2593 				    "General Compiz Options");
2594     }
2595     else
2596     {
2597 	plugin->shortDesc = strdup ("General Options");
2598 	plugin->longDesc  = strdup ("General Compiz Options");
2599     }
2600 #ifdef USE_PROTOBUF
2601     fillBasicInfoIntoPB (plugin, (PluginInfoMetadata *) pluginInfoPBv);
2602 #endif
2603 
2604     initRulesFromRootNode (plugin, node, pluginInfoPBv);
2605     context->plugins = ccsPluginListAppend (context->plugins, plugin);
2606 
2607     return TRUE;
2608 }
2609 
2610 /* End of XML parsing */
2611 
2612 #ifdef USE_PROTOBUF
2613 
2614 // Either pluginMinMetadata or pluginMetadata should be non-NULL
2615 static Bool
loadPluginMetadataFromProtoBuf(char * pbPath,PluginBriefMetadata * pluginMinMetadata,PluginMetadata * pluginMetadata)2616 loadPluginMetadataFromProtoBuf (char *pbPath,
2617 				PluginBriefMetadata *pluginMinMetadata,
2618 				PluginMetadata *pluginMetadata)
2619 {
2620     Bool success = FALSE;
2621 
2622     FILE *pbFile = fopen (pbPath, "rb");
2623     if (pbFile)
2624     {
2625 	google::protobuf::io::FileInputStream inputStream (fileno (pbFile));
2626 	if ((pluginMinMetadata &&
2627 	     pluginMinMetadata->ParseFromZeroCopyStream (&inputStream)) ||
2628 	    (pluginMetadata &&
2629 	     pluginMetadata->ParseFromZeroCopyStream (&inputStream)))
2630 	    success = TRUE;
2631 	inputStream.Close ();
2632     }
2633 
2634     return success;
2635 }
2636 
2637 // Returns TRUE if successfully loads .pb file and .pb is up to date.
2638 static Bool
checkAndLoadProtoBuf(char * pbPath,struct stat * pbStat,struct stat * xmlStat,PluginBriefMetadata * pluginBriefPB)2639 checkAndLoadProtoBuf (char *pbPath,
2640 		      struct stat *pbStat,
2641 		      struct stat *xmlStat,
2642 		      PluginBriefMetadata *pluginBriefPB)
2643 {
2644     const PluginInfoMetadata &pluginInfoPB = pluginBriefPB->info ();
2645 
2646     if (pbStat->st_mtime < xmlStat->st_mtime ||     // is .pb older than .xml?
2647 	!loadPluginMetadataFromProtoBuf (pbPath, pluginBriefPB, NULL) ||
2648 	(!basicMetadata && pluginBriefPB->info ().basic_metadata ()) ||
2649 	pluginInfoPB.pb_abi_version () != PB_ABI_VERSION ||
2650 	pluginInfoPB.time () != (unsigned long)xmlStat->st_mtime ||
2651 	// xml modification time mismatch?
2652 	(pluginInfoPB.locale () != "NONE" &&
2653 	 pluginInfoPB.locale () != shortLocale))
2654     {
2655 	// .pb needs update
2656 	return FALSE;
2657     }
2658     return TRUE;
2659 }
2660 
2661 // Write .pb data to .pb file
2662 static void
writePBFile(char * pbFilePath,PluginMetadata * pluginPB,PluginBriefMetadata * pluginBriefPB,struct stat * xmlStat)2663 writePBFile (char *pbFilePath,
2664 	     PluginMetadata *pluginPB,
2665 	     PluginBriefMetadata *pluginBriefPB,
2666 	     struct stat *xmlStat)
2667 {
2668     if (!createProtoBufCacheDir ())
2669 	return;
2670 
2671     PluginInfoMetadata *pluginInfoPB;
2672 
2673     if (pluginPB)
2674     {
2675 	pluginInfoPB = pluginPB->mutable_info ();
2676 	pluginInfoPB->set_brief_metadata (FALSE);
2677     }
2678     else
2679     {
2680 	pluginInfoPB = pluginBriefPB->mutable_info ();
2681 	pluginInfoPB->set_pb_abi_version (PB_ABI_VERSION);
2682 	pluginInfoPB->set_locale (shortLocale);
2683 	pluginInfoPB->set_time ((unsigned long)xmlStat->st_mtime);
2684 	pluginInfoPB->set_brief_metadata (TRUE);
2685     }
2686 
2687     pluginInfoPB->set_basic_metadata (basicMetadata);
2688 
2689     FILE *pbFile = fopen (pbFilePath, "wb");
2690     if (pbFile)
2691     {
2692 	google::protobuf::io::FileOutputStream
2693 	    outputStream (fileno (pbFile));
2694 	if (pluginPB)
2695 	    pluginPB->SerializeToZeroCopyStream (&outputStream);
2696 	else
2697 	    pluginBriefPB->SerializeToZeroCopyStream (&outputStream);
2698 	outputStream.Close ();
2699     }
2700 }
2701 #endif
2702 
2703 /* Returns TRUE on success. */
2704 static Bool
loadPluginFromXML(CCSContext * context,xmlDoc * doc,char * filename,void * pluginInfoPBv)2705 loadPluginFromXML (CCSContext * context,
2706 		   xmlDoc * doc,
2707 		   char *filename,
2708 		   void * pluginInfoPBv)
2709 {
2710     xmlNode **nodes;
2711     int num;
2712     Bool success = FALSE;
2713 
2714     nodes = getNodesFromXPath (doc, NULL, "/compiz/core", &num);
2715     if (num)
2716     {
2717 	success = addCoreSettingsFromXMLNode (context, nodes[0], filename,
2718 					      pluginInfoPBv);
2719 	free (nodes);
2720 	return success;
2721     }
2722 
2723     nodes = getNodesFromXPath (doc, NULL, "/compiz/plugin", &num);
2724     if (num)
2725     {
2726 	success = addPluginFromXMLNode (context, nodes[0], filename,
2727 					pluginInfoPBv);
2728 	free (nodes);
2729     }
2730     return success;
2731 }
2732 
2733 #ifdef USE_PROTOBUF
2734 static void
updatePBFilePath(CCSContext * context,char * name,char * pbFilePath)2735 updatePBFilePath (CCSContext * context, char *name, char *pbFilePath)
2736 {
2737     CCSPlugin *plugin = ccsFindPlugin (context, name);
2738     if (plugin)
2739     {
2740 	PLUGIN_PRIV (plugin);
2741 
2742 	if (pPrivate->pbFilePath)
2743 	    free (pPrivate->pbFilePath);
2744 	pPrivate->pbFilePath = strdup (pbFilePath);
2745     }
2746 }
2747 #endif
2748 
2749 static void
loadPluginFromXMLFile(CCSContext * context,char * xmlName,char * xmlDirPath)2750 loadPluginFromXMLFile (CCSContext * context, char *xmlName, char *xmlDirPath)
2751 {
2752     char *xmlFilePath = NULL;
2753     char *pbFilePath = NULL;
2754     void *pluginInfoPBv = NULL;
2755 
2756     asprintf (&xmlFilePath, "%s/%s", xmlDirPath, xmlName);
2757     if (!xmlFilePath)
2758     {
2759 	fprintf (stderr, "[ERROR]: Can't allocate memory\n");
2760 	return;
2761     }
2762 
2763 #ifdef USE_PROTOBUF
2764     char *name = NULL;
2765     struct stat xmlStat;
2766     Bool removePB = FALSE;
2767 
2768     if (usingProtobuf)
2769     {
2770 	if (stat (xmlFilePath, &xmlStat))
2771 	{
2772 	    free (xmlFilePath);
2773 	    return;
2774 	}
2775 
2776 	// Check if the corresponding .pb exists in cache
2777 	Bool error = TRUE;
2778 	struct stat pbStat;
2779 
2780 	name = strndup (xmlName, strlen (xmlName) - 4);
2781 	if (!name)
2782 	{
2783 	    fprintf (stderr, "[ERROR]: Can't allocate memory\n");
2784 	    free (xmlFilePath);
2785 	    return;
2786 	}
2787 
2788 	if (createProtoBufCacheDir () &&
2789 	    metadataCacheDir.length () > 0)
2790 	{
2791 	    asprintf (&pbFilePath, "%s/%s.pb", metadataCacheDir.c_str (), name);
2792 	    if (!pbFilePath)
2793 	    {
2794 		fprintf (stderr, "[ERROR]: Can't allocate memory\n");
2795 		free (xmlFilePath);
2796 		free (name);
2797 		return;
2798 	    }
2799 	    error = stat (pbFilePath, &pbStat);
2800 	}
2801 
2802 	if (!error)
2803 	{
2804 	    if (checkAndLoadProtoBuf (pbFilePath, &pbStat, &xmlStat,
2805 				      &persistentPluginBriefPB))
2806 	    {
2807 		// Found and loaded .pb
2808 		if (!strcmp (name, "core"))
2809 		    addCoreSettingsFromPB (context,
2810 					   persistentPluginBriefPB.info (),
2811 					   pbFilePath, xmlFilePath);
2812 		else
2813 		    addPluginFromPB (context, persistentPluginBriefPB.info (),
2814 				     pbFilePath, xmlFilePath);
2815 
2816 		updatePBFilePath (context, name, pbFilePath);
2817 
2818 		free (xmlFilePath);
2819 		free (pbFilePath);
2820 		free (name);
2821 		return;
2822 	    }
2823 	    else
2824 	    {
2825 		removePB = TRUE;
2826 	    }
2827 	}
2828 	persistentPluginBriefPB.Clear ();
2829 	pluginInfoPBv = persistentPluginBriefPB.mutable_info ();
2830     }
2831 #endif
2832 
2833     // Load from .xml
2834     FILE *fp = fopen (xmlFilePath, "r");
2835     Bool xmlLoaded = FALSE;
2836 
2837     if (fp)
2838     {
2839 	fclose (fp);
2840 	xmlDoc *doc = xmlReadFile (xmlFilePath, NULL, 0);
2841 	if (doc)
2842 	{
2843 	    xmlLoaded = loadPluginFromXML (context, doc, xmlFilePath,
2844 					   pluginInfoPBv);
2845 	    xmlFreeDoc (doc);
2846 	}
2847     }
2848     free (xmlFilePath);
2849 
2850 #ifdef USE_PROTOBUF
2851     if (usingProtobuf && xmlLoaded)
2852     {
2853 	if (removePB)
2854 	    remove (pbFilePath); // Attempt to remove .pb
2855 	writePBFile (pbFilePath, NULL, &persistentPluginBriefPB, &xmlStat);
2856 	updatePBFilePath (context, name, pbFilePath);
2857     }
2858 
2859     if (pbFilePath)
2860 	free (pbFilePath);
2861     if (name)
2862 	free (name);
2863 #endif
2864 }
2865 
2866 static void
loadPluginsFromXMLFiles(CCSContext * context,char * path)2867 loadPluginsFromXMLFiles (CCSContext * context, char *path)
2868 {
2869     struct dirent **nameList;
2870     int nFile, i;
2871 
2872     if (!path)
2873 	return;
2874 
2875     nFile = scandir (path, &nameList, pluginXMLFilter, NULL);
2876 
2877     if (nFile <= 0)
2878 	return;
2879 
2880     for (i = 0; i < nFile; i++)
2881     {
2882 	loadPluginFromXMLFile (context, nameList[i]->d_name, path);
2883 	free (nameList[i]);
2884     }
2885     free (nameList);
2886 }
2887 
2888 static void
addPluginNamed(CCSContext * context,char * name)2889 addPluginNamed (CCSContext * context, char *name)
2890 {
2891     CCSPlugin *plugin;
2892     CCSPluginPrivate *pPrivate;
2893 
2894     if (ccsFindPlugin (context, name))
2895 	return;
2896 
2897     if (!strcmp (name, "ini") || !strcmp (name, "gconf") ||
2898 	!strcmp (name, "ccp") || !strcmp (name, "kconfig"))
2899 	return;
2900 
2901     plugin = (CCSPlugin *) calloc (1, sizeof (CCSPlugin));
2902     if (!plugin)
2903 	return;
2904 
2905     pPrivate = (CCSPluginPrivate *) calloc (1, sizeof (CCSPluginPrivate));
2906     if (!pPrivate)
2907     {
2908 	free (plugin);
2909 	return;
2910     }
2911 
2912     plugin->ccsPrivate = (void *) pPrivate;
2913 
2914     plugin->context = context;
2915     plugin->name = strdup (name);
2916 
2917     if (!plugin->shortDesc)
2918 	plugin->shortDesc = strdup (name);
2919     if (!plugin->longDesc)
2920 	plugin->longDesc = strdup (name);
2921     if (!plugin->category)
2922 	plugin->category = strdup ("");
2923 
2924     pPrivate->loaded = TRUE;
2925     collateGroups (pPrivate);
2926     context->plugins = ccsPluginListAppend (context->plugins, plugin);
2927 }
2928 
2929 static void
loadPluginsFromName(CCSContext * context,char * path)2930 loadPluginsFromName (CCSContext * context, char *path)
2931 {
2932     struct dirent **nameList;
2933     int nFile, i;
2934 
2935     if (!path)
2936 	return;
2937 
2938     nFile = scandir (path, &nameList, pluginNameFilter, NULL);
2939     if (nFile <= 0)
2940 	return;
2941 
2942     for (i = 0; i < nFile; i++)
2943     {
2944 	char name[1024];
2945 	sscanf (nameList[i]->d_name, "lib%s", name);
2946 	if (strlen (name) > 3)
2947 	    name[strlen (name) - 3] = 0;
2948 	free (nameList[i]);
2949 	addPluginNamed (context, name);
2950     }
2951     free (nameList);
2952 }
2953 
2954 #ifdef USE_PROTOBUF
2955 static inline void
initPBLoading()2956 initPBLoading ()
2957 {
2958     // Update usingProtobuf with the COMPIZ_NO_PROTOBUF environment variable
2959     char *compizNoProtobuf = getenv ("COMPIZ_NO_PROTOBUF");
2960     usingProtobuf = !(compizNoProtobuf &&
2961 		      (strcasecmp (compizNoProtobuf, "1") == 0 ||
2962 		       strcasecmp (compizNoProtobuf, "yes") == 0 ||
2963 		       strcasecmp (compizNoProtobuf, "true") == 0));
2964     if (usingProtobuf)
2965     {
2966 	// Verify that the version of the library that we linked against is
2967 	// compatible with the version of the headers we compiled against.
2968 	GOOGLE_PROTOBUF_VERIFY_VERSION;
2969     }
2970 }
2971 #endif
2972 
2973 Bool
ccsLoadPlugin(CCSContext * context,char * name)2974 ccsLoadPlugin (CCSContext * context, char *name)
2975 {
2976 #ifdef USE_PROTOBUF
2977     initPBLoading ();
2978 #endif
2979 
2980     char *xmlDirPath = NULL;
2981     char *xmlName = NULL;
2982     asprintf (&xmlName, "%s.xml", name);
2983 
2984     if (xmlName)
2985     {
2986 	char *home = getenv ("HOME");
2987 	if (home && strlen (home))
2988 	{
2989 	    char *home = getenv ("HOME");
2990 	    asprintf (&xmlDirPath, "%s/.compiz/metadata", home);
2991 	    if (xmlDirPath)
2992 	    {
2993 		loadPluginFromXMLFile (context, xmlName, xmlDirPath);
2994 		free (xmlDirPath);
2995 	    }
2996 	}
2997 
2998 	loadPluginFromXMLFile (context, xmlName, (char *) METADATADIR);
2999 	free (xmlName);
3000     }
3001 
3002     return (ccsFindPlugin (context, name) != NULL);
3003 }
3004 
3005 void
ccsLoadPlugins(CCSContext * context)3006 ccsLoadPlugins (CCSContext * context)
3007 {
3008     D (D_FULL, "Adding plugins\n");
3009 
3010 #ifdef USE_PROTOBUF
3011     initPBLoading ();
3012 #endif
3013 
3014     char *home = getenv ("HOME");
3015     if (home && strlen (home))
3016     {
3017 	char *homeplugins = NULL;
3018 	asprintf (&homeplugins, "%s/.compiz/metadata", home);
3019 	if (homeplugins)
3020 	{
3021 	    loadPluginsFromXMLFiles (context, homeplugins);
3022 	    free (homeplugins);
3023 	}
3024     }
3025     loadPluginsFromXMLFiles (context, (char *)METADATADIR);
3026 
3027     if (home && strlen (home))
3028     {
3029 	char *homeplugins = NULL;
3030 	asprintf (&homeplugins, "%s/.compiz/plugins", home);
3031 	if (homeplugins)
3032 	{
3033 	    loadPluginsFromName (context, homeplugins);
3034 	    free (homeplugins);
3035 	}
3036     }
3037     loadPluginsFromName (context, (char *)PLUGINDIR);
3038 }
3039 
3040 static void
loadOptionsStringExtensionsFromXML(CCSPlugin * plugin,void * pluginPBv,struct stat * xmlStat)3041 loadOptionsStringExtensionsFromXML (CCSPlugin * plugin,
3042 				    void * pluginPBv,
3043 				    struct stat *xmlStat)
3044 {
3045     PLUGIN_PRIV (plugin);
3046 
3047     xmlDoc *doc = NULL;
3048     xmlNode **nodes;
3049     int num;
3050 
3051     if (stat (pPrivate->xmlFile, xmlStat))
3052 	return;
3053 
3054     FILE *fp = fopen (pPrivate->xmlFile, "r");
3055     if (!fp)
3056 	return;
3057 
3058     fclose (fp);
3059     doc = xmlReadFile (pPrivate->xmlFile, NULL, 0);
3060 
3061     nodes = getNodesFromXPath (doc, NULL, pPrivate->xmlPath, &num);
3062     if (num)
3063     {
3064 	initOptionsFromRootNode (plugin, nodes[0], pluginPBv);
3065 	if (!basicMetadata)
3066 	    initStringExtensionsFromRootNode (plugin, nodes[0], pluginPBv);
3067 	free (nodes);
3068     }
3069     if (doc)
3070 	xmlFreeDoc (doc);
3071 }
3072 
3073 void
ccsLoadPluginSettings(CCSPlugin * plugin)3074 ccsLoadPluginSettings (CCSPlugin * plugin)
3075 {
3076     Bool ignoreXML = FALSE;
3077     Bool loadedAtLeastBriefPB = FALSE;
3078     void *pluginPBToWrite = NULL;
3079 
3080 #ifdef USE_PROTOBUF
3081     initPBLoading ();
3082 #endif
3083 
3084     PLUGIN_PRIV (plugin);
3085 
3086     if (pPrivate->loaded)
3087 	return;
3088 
3089     pPrivate->loaded = TRUE;
3090     D (D_FULL, "Initializing %s options...", plugin->name);
3091 
3092 #ifdef USE_PROTOBUF
3093     if (usingProtobuf && pPrivate->pbFilePath)
3094     {
3095 	loadedAtLeastBriefPB =
3096 	    loadPluginMetadataFromProtoBuf (pPrivate->pbFilePath,
3097 					    NULL, &persistentPluginPB);
3098 	if (loadedAtLeastBriefPB)
3099 	{
3100 	    if (!persistentPluginPB.info ().brief_metadata () &&
3101 		(basicMetadata ||
3102 		 !persistentPluginPB.info ().basic_metadata ()))
3103 	    {
3104 		initOptionsFromPB (plugin, persistentPluginPB);
3105 		if (!basicMetadata)
3106 		    initStringExtensionsFromPB (plugin, persistentPluginPB);
3107 		ignoreXML = TRUE;
3108 	    }
3109 	    else
3110 		pluginPBToWrite = &persistentPluginPB;
3111 	}
3112 	else
3113 	    pluginPBToWrite = &persistentPluginPB;
3114     }
3115 #endif
3116 
3117     struct stat xmlStat;
3118 
3119     // Load from .xml
3120     if (!ignoreXML && pPrivate->xmlFile)
3121 	loadOptionsStringExtensionsFromXML (plugin, pluginPBToWrite, &xmlStat);
3122 
3123 #ifdef USE_PROTOBUF
3124     if (pluginPBToWrite && pPrivate->pbFilePath && loadedAtLeastBriefPB)
3125 	writePBFile (pPrivate->pbFilePath, (PluginMetadata *) pluginPBToWrite,
3126 		     NULL, &xmlStat);
3127 #endif
3128     D (D_FULL, "done\n");
3129 
3130     collateGroups (pPrivate);
3131     ccsReadPluginSettings (plugin);
3132 }
3133 
3134