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