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 }