1 /*
2  * Copyright © 2007 Dennis Kasprzyk
3  * Copyright © 2007 Novell, Inc.
4  *
5  * Permission to use, copy, modify, distribute, and sell this software
6  * and its documentation for any purpose is hereby granted without
7  * fee, provided that the above copyright notice appear in all copies
8  * and that both that copyright notice and this permission notice
9  * appear in supporting documentation, and that the name of
10  * Dennis Kasprzyk not be used in advertising or publicity pertaining to
11  * distribution of the software without specific, written prior permission.
12  * Dennis Kasprzyk makes no representations about the suitability of this
13  * software for any purpose. It is provided "as is" without express or
14  * implied warranty.
15  *
16  * DENNIS KASPRZYK DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
18  * NO EVENT SHALL DENNIS KASPRZYK BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
20  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
21  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
22  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Authors: Dennis Kasprzyk <onestone@deltatauchi.de>
25  *          David Reveman <davidr@novell.com>
26  */
27 
28 #include <string.h>
29 #include <libxml/tree.h>
30 #include <libxml/xpath.h>
31 #include <libxml/xpathInternals.h>
32 #include <locale.h>
33 
34 #include <compiz-core.h>
35 
36 #define HOME_METADATADIR ".compiz/metadata"
37 #define EXTENSION ".xml"
38 
39 Bool
compInitMetadata(CompMetadata * metadata)40 compInitMetadata (CompMetadata *metadata)
41 {
42     metadata->path = strdup ("core");
43     if (!metadata->path)
44 	return FALSE;
45 
46     metadata->doc  = NULL;
47     metadata->nDoc = 0;
48 
49     return TRUE;
50 }
51 
52 Bool
compInitPluginMetadata(CompMetadata * metadata,const char * plugin)53 compInitPluginMetadata (CompMetadata *metadata,
54 			const char   *plugin)
55 {
56     char str[1024];
57 
58     snprintf (str, 1024, "plugin[@name=\"%s\"]", plugin);
59 
60     metadata->path = strdup (str);
61     if (!metadata->path)
62 	return FALSE;
63 
64     metadata->doc  = NULL;
65     metadata->nDoc = 0;
66 
67     return TRUE;
68 }
69 
70 void
compFiniMetadata(CompMetadata * metadata)71 compFiniMetadata (CompMetadata *metadata)
72 {
73     int i;
74 
75     for (i = 0; i < metadata->nDoc; i++)
76 	xmlFreeDoc (metadata->doc[i]);
77 
78     if (metadata->doc)
79 	free (metadata->doc);
80 
81     free (metadata->path);
82 }
83 
84 static xmlDoc *
readXmlFile(const char * path,const char * name)85 readXmlFile (const char	*path,
86 	     const char	*name)
87 {
88     char   *file;
89     int    length = strlen (name) + strlen (EXTENSION) + 1;
90     xmlDoc *doc = NULL;
91     FILE   *fp;
92 
93     if (path)
94 	length += strlen (path) + 1;
95 
96     file = malloc (length);
97     if (!file)
98 	return NULL;
99 
100     if (path)
101 	sprintf (file, "%s/%s%s", path, name, EXTENSION);
102     else
103 	sprintf (file, "%s%s", name, EXTENSION);
104 
105     fp = fopen (file, "r");
106     if (!fp)
107     {
108 	free (file);
109 	return NULL;
110     }
111 
112     fclose (fp);
113 
114     doc = xmlReadFile (file, NULL, 0);
115 
116     free (file);
117 
118     return doc;
119 }
120 
121 static Bool
addMetadataFromFilename(CompMetadata * metadata,const char * path,const char * file)122 addMetadataFromFilename (CompMetadata *metadata,
123 			 const char   *path,
124 			 const char   *file)
125 {
126     xmlDoc **d, *doc;
127 
128     doc = readXmlFile (path, file);
129     if (!doc)
130 	return FALSE;
131 
132     d = realloc (metadata->doc, (metadata->nDoc + 1) * sizeof (xmlDoc *));
133     if (!d)
134     {
135 	xmlFreeDoc (doc);
136 	return FALSE;
137     }
138 
139     d[metadata->nDoc++] = doc;
140     metadata->doc = d;
141 
142     return TRUE;
143 }
144 
145 Bool
compAddMetadataFromFile(CompMetadata * metadata,const char * file)146 compAddMetadataFromFile (CompMetadata *metadata,
147 			 const char   *file)
148 {
149     char *home;
150     Bool status = FALSE;
151 
152     home = getenv ("HOME");
153     if (home)
154     {
155 	char *path;
156 
157 	path = malloc (strlen (home) + strlen (HOME_METADATADIR) + 2);
158 	if (path)
159 	{
160 	    sprintf (path, "%s/%s", home, HOME_METADATADIR);
161 	    status |= addMetadataFromFilename (metadata, path, file);
162 	    free (path);
163 	}
164     }
165 
166     status |= addMetadataFromFilename (metadata, METADATADIR, file);
167     if (!status)
168     {
169 	compLogMessage ("core", CompLogLevelWarn,
170 			"Unable to parse XML metadata from file \"%s%s\"",
171 			file, EXTENSION);
172 
173 	return FALSE;
174     }
175 
176     return TRUE;
177 }
178 
179 Bool
compAddMetadataFromString(CompMetadata * metadata,const char * string)180 compAddMetadataFromString (CompMetadata *metadata,
181 			   const char   *string)
182 {
183     xmlDoc **d, *doc;
184 
185     doc = xmlReadMemory (string, strlen (string), NULL, NULL, 0);
186     if (!doc)
187     {
188 	compLogMessage ("core", CompLogLevelWarn,
189 			"Unable to parse XML metadata");
190 
191 	return FALSE;
192     }
193 
194     d = realloc (metadata->doc, (metadata->nDoc + 1) * sizeof (xmlDoc *));
195     if (!d)
196     {
197 	xmlFreeDoc (doc);
198 	return FALSE;
199     }
200 
201     d[metadata->nDoc++] = doc;
202     metadata->doc = d;
203 
204     return TRUE;
205 }
206 
207 Bool
compAddMetadataFromIO(CompMetadata * metadata,xmlInputReadCallback ioread,xmlInputCloseCallback ioclose,void * ioctx)208 compAddMetadataFromIO (CompMetadata	     *metadata,
209 		       xmlInputReadCallback  ioread,
210 		       xmlInputCloseCallback ioclose,
211 		       void		     *ioctx)
212 {
213     xmlDoc **d, *doc;
214 
215     doc = xmlReadIO (ioread, ioclose, ioctx, NULL, NULL, 0);
216     if (!doc)
217     {
218 	compLogMessage ("core", CompLogLevelWarn,
219 			"Unable to parse XML metadata");
220 
221 	return FALSE;
222     }
223 
224     d = realloc (metadata->doc, (metadata->nDoc + 1) * sizeof (xmlDoc *));
225     if (!d)
226     {
227 	xmlFreeDoc (doc);
228 	return FALSE;
229     }
230 
231     d[metadata->nDoc++] = doc;
232     metadata->doc = d;
233 
234     return TRUE;
235 }
236 
237 typedef struct _CompIOCtx {
238     int				 offset;
239     const char			 *name;
240     const CompMetadataOptionInfo *displayOInfo;
241     int				 nDisplayOInfo;
242     const CompMetadataOptionInfo *screenOInfo;
243     int				 nScreenOInfo;
244 } CompIOCtx;
245 
246 static int
readPluginXmlCallback(void * context,char * buffer,int length)247 readPluginXmlCallback (void *context,
248 		       char *buffer,
249 		       int  length)
250 {
251     CompIOCtx *ctx = (CompIOCtx *) context;
252     int	      offset = ctx->offset;
253     int	      i, j;
254 
255     i = compReadXmlChunk ("<compiz><plugin name=\"", &offset, buffer, length);
256     i += compReadXmlChunk (ctx->name, &offset, buffer + i, length - i);
257     i += compReadXmlChunk ("\">", &offset, buffer + i, length - i);
258 
259     if (ctx->nDisplayOInfo)
260     {
261 	i += compReadXmlChunk ("<display>", &offset, buffer + i, length - i);
262 
263 	for (j = 0; j < ctx->nDisplayOInfo; j++)
264 	    i += compReadXmlChunkFromMetadataOptionInfo (&ctx->displayOInfo[j],
265 							 &offset,
266 							 buffer + i,
267 							 length - i);
268 
269 	i += compReadXmlChunk ("</display>", &offset, buffer + i, length - i);
270     }
271 
272     if (ctx->nScreenOInfo)
273     {
274 	i += compReadXmlChunk ("<screen>", &offset, buffer + i, length - i);
275 
276 	for (j = 0; j < ctx->nScreenOInfo; j++)
277 	    i += compReadXmlChunkFromMetadataOptionInfo (&ctx->screenOInfo[j],
278 							 &offset,
279 							 buffer + i,
280 							 length - i);
281 
282 	i += compReadXmlChunk ("</screen>", &offset, buffer + i, length - i);
283     }
284 
285     i += compReadXmlChunk ("</plugin></compiz>", &offset, buffer + i,
286 			   length - i);
287 
288     if (!offset && length > i)
289 	buffer[i++] = '\0';
290 
291     ctx->offset += i;
292 
293     return i;
294 }
295 
296 Bool
compInitPluginMetadataFromInfo(CompMetadata * metadata,const char * plugin,const CompMetadataOptionInfo * displayOptionInfo,int nDisplayOptionInfo,const CompMetadataOptionInfo * screenOptionInfo,int nScreenOptionInfo)297 compInitPluginMetadataFromInfo (CompMetadata		     *metadata,
298 				const char		     *plugin,
299 				const CompMetadataOptionInfo *displayOptionInfo,
300 				int			     nDisplayOptionInfo,
301 				const CompMetadataOptionInfo *screenOptionInfo,
302 				int			     nScreenOptionInfo)
303 {
304     if (!compInitPluginMetadata (metadata, plugin))
305 	return FALSE;
306 
307     if (nDisplayOptionInfo || nScreenOptionInfo)
308     {
309 	CompIOCtx ctx;
310 
311 	ctx.offset	  = 0;
312 	ctx.name	  = plugin;
313 	ctx.displayOInfo  = displayOptionInfo;
314 	ctx.nDisplayOInfo = nDisplayOptionInfo;
315 	ctx.screenOInfo   = screenOptionInfo;
316 	ctx.nScreenOInfo  = nScreenOptionInfo;
317 
318 	if (!compAddMetadataFromIO (metadata,
319 				    readPluginXmlCallback, NULL,
320 				    (void *) &ctx))
321 	{
322 	    compFiniMetadata (metadata);
323 	    return FALSE;
324 	}
325     }
326 
327     return TRUE;
328 }
329 
330 typedef struct _CompXPath {
331     xmlXPathObjectPtr  obj;
332     xmlXPathContextPtr ctx;
333     xmlDocPtr	       doc;
334 } CompXPath;
335 
336 static Bool
initXPathFromMetadataPath(CompXPath * xPath,CompMetadata * metadata,const xmlChar * path)337 initXPathFromMetadataPath (CompXPath	 *xPath,
338 			   CompMetadata  *metadata,
339 			   const xmlChar *path)
340 {
341     xmlXPathObjectPtr  obj;
342     xmlXPathContextPtr ctx;
343     int		       i;
344 
345     for (i = 0; i < metadata->nDoc; i++)
346     {
347 	ctx = xmlXPathNewContext (metadata->doc[i]);
348 	if (ctx)
349 	{
350 	    obj = xmlXPathEvalExpression (path, ctx);
351 	    if (obj)
352 	    {
353 		if (obj->nodesetval && obj->nodesetval->nodeNr)
354 		{
355 		    xPath->ctx = ctx;
356 		    xPath->obj = obj;
357 		    xPath->doc = metadata->doc[i];
358 
359 		    return TRUE;
360 		}
361 
362 		xmlXPathFreeObject (obj);
363 	    }
364 
365 	    xmlXPathFreeContext (ctx);
366 	}
367     }
368 
369     return FALSE;
370 }
371 
372 static Bool
initXPathFromMetadataPathElement(CompXPath * xPath,CompMetadata * metadata,const xmlChar * path,const xmlChar * element)373 initXPathFromMetadataPathElement (CompXPath	*xPath,
374 				  CompMetadata  *metadata,
375 				  const xmlChar *path,
376 				  const xmlChar *element)
377 {
378     char str[1024];
379 
380     snprintf (str, 1024, "%s/%s", path, element);
381 
382     return initXPathFromMetadataPath (xPath, metadata, BAD_CAST str);
383 }
384 
385 static void
finiXPath(CompXPath * xPath)386 finiXPath (CompXPath *xPath)
387 {
388     xmlXPathFreeObject (xPath->obj);
389     xmlXPathFreeContext (xPath->ctx);
390 }
391 
392 static CompOptionType
getOptionType(char * name)393 getOptionType (char *name)
394 {
395     static struct _TypeMap {
396 	char	       *name;
397 	CompOptionType type;
398     } map[] = {
399 	{ "int",    CompOptionTypeInt    },
400 	{ "float",  CompOptionTypeFloat  },
401 	{ "string", CompOptionTypeString },
402 	{ "color",  CompOptionTypeColor  },
403 	{ "action", CompOptionTypeAction },
404 	{ "key",    CompOptionTypeKey    },
405 	{ "button", CompOptionTypeButton },
406 	{ "edge",   CompOptionTypeEdge   },
407 	{ "bell",   CompOptionTypeBell   },
408 	{ "match",  CompOptionTypeMatch  },
409 	{ "list",   CompOptionTypeList   }
410     };
411     int i;
412 
413     for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
414 	if (strcasecmp (name, map[i].name) == 0)
415 	    return map[i].type;
416 
417     return CompOptionTypeBool;
418 }
419 
420 static void
initBoolValue(CompOptionValue * v,xmlDocPtr doc,xmlNodePtr node)421 initBoolValue (CompOptionValue *v,
422 	       xmlDocPtr       doc,
423 	       xmlNodePtr      node)
424 {
425     xmlChar *value;
426 
427     v->b = FALSE;
428 
429     if (!doc)
430 	return;
431 
432     value = xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
433     if (value)
434     {
435 	if (strcasecmp ((char *) value, "true") == 0)
436 	    v->b = TRUE;
437 
438 	xmlFree (value);
439     }
440 }
441 
442 static void
initIntValue(CompOptionValue * v,CompOptionRestriction * r,xmlDocPtr doc,xmlNodePtr node)443 initIntValue (CompOptionValue	    *v,
444 	      CompOptionRestriction *r,
445 	      xmlDocPtr		    doc,
446 	      xmlNodePtr	    node)
447 {
448     xmlChar *value;
449 
450     v->i = (r->i.min + r->i.max) / 2;
451 
452     if (!doc)
453 	return;
454 
455     value = xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
456     if (value)
457     {
458 	int i = strtol ((char *) value, NULL, 0);
459 
460 	if (i >= r->i.min && i <= r->i.max)
461 	    v->i = i;
462 
463 	xmlFree (value);
464     }
465 }
466 
467 static void
initFloatValue(CompOptionValue * v,CompOptionRestriction * r,xmlDocPtr doc,xmlNodePtr node)468 initFloatValue (CompOptionValue	      *v,
469 		CompOptionRestriction *r,
470 		xmlDocPtr	      doc,
471 		xmlNodePtr	      node)
472 {
473     xmlChar *value;
474     char *loc;
475 
476     v->f = (r->f.min + r->f.max) / 2;
477 
478     if (!doc)
479 	return;
480 
481     loc = setlocale (LC_NUMERIC, "C");
482     value = xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
483     if (value)
484     {
485 	float f = strtod ((char *) value, NULL);
486 
487 	if (f >= r->f.min && f <= r->f.max)
488 	    v->f = f;
489 
490 	xmlFree (value);
491     }
492     setlocale (LC_NUMERIC, loc);
493 }
494 
495 static void
initStringValue(CompOptionValue * v,CompOptionRestriction * r,xmlDocPtr doc,xmlNodePtr node)496 initStringValue (CompOptionValue       *v,
497 		 CompOptionRestriction *r,
498 		 xmlDocPtr	       doc,
499 		 xmlNodePtr	       node)
500 {
501     xmlChar *value;
502 
503     v->s = strdup ("");
504 
505     if (!doc)
506 	return;
507 
508     value = xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
509     if (value)
510     {
511 	free (v->s);
512 	v->s = strdup ((char *) value);
513 
514 	xmlFree (value);
515     }
516 }
517 
518 static void
initColorValue(CompOptionValue * v,xmlDocPtr doc,xmlNodePtr node)519 initColorValue (CompOptionValue *v,
520 		xmlDocPtr       doc,
521 		xmlNodePtr      node)
522 {
523     xmlNodePtr child;
524 
525     v->c[0] = 0x0000;
526     v->c[1] = 0x0000;
527     v->c[2] = 0x0000;
528     v->c[3] = 0xffff;
529 
530     if (!doc)
531 	return;
532 
533     for (child = node->xmlChildrenNode; child; child = child->next)
534     {
535 	xmlChar *value;
536 	int	index;
537 
538 	if (!xmlStrcmp (child->name, BAD_CAST "red"))
539 	    index = 0;
540 	else if (!xmlStrcmp (child->name, BAD_CAST "green"))
541 	    index = 1;
542 	else if (!xmlStrcmp (child->name, BAD_CAST "blue"))
543 	    index = 2;
544 	else if (!xmlStrcmp (child->name, BAD_CAST "alpha"))
545 	    index = 3;
546 	else
547 	    continue;
548 
549 	value = xmlNodeListGetString (child->doc, child->xmlChildrenNode, 1);
550 	if (value)
551 	{
552 	    int color = strtol ((char *) value, NULL , 0);
553 
554 	    v->c[index] = MAX (0, MIN (0xffff, color));
555 
556 	    xmlFree (value);
557 	}
558     }
559 }
560 
561 static void
initActionValue(CompDisplay * d,CompOptionValue * v,CompActionState state,xmlDocPtr doc,xmlNodePtr node)562 initActionValue (CompDisplay	 *d,
563 		 CompOptionValue *v,
564 		 CompActionState state,
565 		 xmlDocPtr       doc,
566 		 xmlNodePtr      node)
567 {
568     memset (&v->action, 0, sizeof (v->action));
569 
570     v->action.state = state;
571 }
572 
573 static void
initKeyValue(CompDisplay * d,CompOptionValue * v,CompActionState state,xmlDocPtr doc,xmlNodePtr node)574 initKeyValue (CompDisplay     *d,
575 	      CompOptionValue *v,
576 	      CompActionState state,
577 	      xmlDocPtr       doc,
578 	      xmlNodePtr      node)
579 {
580     xmlChar *value;
581 
582     memset (&v->action, 0, sizeof (v->action));
583 
584     v->action.state = state | CompActionStateInitKey;
585 
586     if (!doc)
587 	return;
588 
589     value = xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
590     if (value)
591     {
592 	char *binding = (char *) value;
593 
594 	if (strcasecmp (binding, "disabled") && *binding)
595 	    stringToKeyAction (d, binding, &v->action);
596 
597 	xmlFree (value);
598     }
599 
600     if (state & CompActionStateAutoGrab)
601     {
602 	CompScreen *s;
603 
604 	for (s = d->screens; s; s = s->next)
605 	    addScreenAction (s, &v->action);
606     }
607 }
608 
609 static void
initButtonValue(CompDisplay * d,CompOptionValue * v,CompActionState state,xmlDocPtr doc,xmlNodePtr node)610 initButtonValue (CompDisplay     *d,
611 		 CompOptionValue *v,
612 		 CompActionState state,
613 		 xmlDocPtr       doc,
614 		 xmlNodePtr      node)
615 {
616     xmlChar *value;
617 
618     memset (&v->action, 0, sizeof (v->action));
619 
620     v->action.state = state | CompActionStateInitButton |
621 	CompActionStateInitEdge;
622 
623     if (!doc)
624 	return;
625 
626     value = xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
627     if (value)
628     {
629 	char *binding = (char *) value;
630 
631 	if (strcasecmp (binding, "disabled") && *binding)
632 	    stringToButtonAction (d, binding, &v->action);
633 
634 	xmlFree (value);
635     }
636 
637     if (state & CompActionStateAutoGrab)
638     {
639 	CompScreen *s;
640 
641 	for (s = d->screens; s; s = s->next)
642 	    addScreenAction (s, &v->action);
643     }
644 }
645 
646 static void
initEdgeValue(CompDisplay * d,CompOptionValue * v,CompActionState state,xmlDocPtr doc,xmlNodePtr node)647 initEdgeValue (CompDisplay     *d,
648 	       CompOptionValue *v,
649 	       CompActionState state,
650 	       xmlDocPtr       doc,
651 	       xmlNodePtr      node)
652 {
653     xmlNodePtr child;
654     xmlChar    *value;
655 
656     memset (&v->action, 0, sizeof (v->action));
657 
658     v->action.state = state | CompActionStateInitEdge;
659 
660     if (!doc)
661 	return;
662 
663     for (child = node->xmlChildrenNode; child; child = child->next)
664     {
665 	value = xmlGetProp (child, BAD_CAST "name");
666 	if (value)
667 	{
668 	    int i;
669 
670 	    for (i = 0; i < SCREEN_EDGE_NUM; i++)
671 		if (strcasecmp ((char *) value, edgeToString (i)) == 0)
672 		    v->action.edgeMask |= (1 << i);
673 
674 	    xmlFree (value);
675 	}
676     }
677 
678     if (state & CompActionStateAutoGrab)
679     {
680 	CompScreen *s;
681 
682 	for (s = d->screens; s; s = s->next)
683 	    addScreenAction (s, &v->action);
684     }
685 }
686 
687 static void
initBellValue(CompDisplay * d,CompOptionValue * v,CompActionState state,xmlDocPtr doc,xmlNodePtr node)688 initBellValue (CompDisplay     *d,
689 	       CompOptionValue *v,
690 	       CompActionState state,
691 	       xmlDocPtr       doc,
692 	       xmlNodePtr      node)
693 {
694     xmlChar *value;
695 
696     memset (&v->action, 0, sizeof (v->action));
697 
698     v->action.state = state | CompActionStateInitBell;
699 
700     if (!doc)
701 	return;
702 
703     value = xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
704     if (value)
705     {
706 	if (strcasecmp ((char *) value, "true") == 0)
707 	    v->action.bell = TRUE;
708 
709 	xmlFree (value);
710     }
711 }
712 
713 static void
initMatchValue(CompDisplay * d,CompOptionValue * v,Bool helper,xmlDocPtr doc,xmlNodePtr node)714 initMatchValue (CompDisplay     *d,
715 		CompOptionValue *v,
716 		Bool		helper,
717 		xmlDocPtr       doc,
718 		xmlNodePtr      node)
719 {
720     xmlChar *value;
721 
722     matchInit (&v->match);
723 
724     if (!doc)
725 	return;
726 
727     value = xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
728     if (value)
729     {
730 	matchAddFromString (&v->match, (char *) value);
731 	xmlFree (value);
732     }
733 
734     if (!helper)
735 	matchUpdate (d, &v->match);
736 }
737 
738 static void
initListValue(CompDisplay * d,CompOptionValue * v,CompOptionRestriction * r,CompActionState state,Bool helper,xmlDocPtr doc,xmlNodePtr node)739 initListValue (CompDisplay	     *d,
740 	       CompOptionValue	     *v,
741 	       CompOptionRestriction *r,
742 	       CompActionState	     state,
743 	       Bool		     helper,
744 	       xmlDocPtr	     doc,
745 	       xmlNodePtr	     node)
746 {
747     xmlNodePtr child;
748 
749     v->list.value  = NULL;
750     v->list.nValue = 0;
751 
752     if (!doc)
753 	return;
754 
755     for (child = node->xmlChildrenNode; child; child = child->next)
756     {
757 	CompOptionValue *value;
758 
759 	if (xmlStrcmp (child->name, BAD_CAST "value"))
760 	    continue;
761 
762 	value = realloc (v->list.value,
763 			 sizeof (CompOptionValue) * (v->list.nValue + 1));
764 	if (value)
765 	{
766 	    switch (v->list.type) {
767 	    case CompOptionTypeBool:
768 		initBoolValue (&value[v->list.nValue], doc, child);
769 		break;
770 	    case CompOptionTypeInt:
771 		initIntValue (&value[v->list.nValue], r, doc, child);
772 		break;
773 	    case CompOptionTypeFloat:
774 		initFloatValue (&value[v->list.nValue], r, doc, child);
775 		break;
776 	    case CompOptionTypeString:
777 		initStringValue (&value[v->list.nValue], r, doc, child);
778 		break;
779 	    case CompOptionTypeColor:
780 		initColorValue (&value[v->list.nValue], doc, child);
781 		break;
782 	    case CompOptionTypeAction:
783 		initActionValue (d, &value[v->list.nValue], state, doc, child);
784 		break;
785 	    case CompOptionTypeKey:
786 		initKeyValue (d, &value[v->list.nValue], state, doc, child);
787 		break;
788 	    case CompOptionTypeButton:
789 		initButtonValue (d, &value[v->list.nValue], state, doc, child);
790 		break;
791 	    case CompOptionTypeEdge:
792 		initEdgeValue (d, &value[v->list.nValue], state, doc, child);
793 		break;
794 	    case CompOptionTypeBell:
795 		initBellValue (d, &value[v->list.nValue], state, doc, child);
796 		break;
797 	    case CompOptionTypeMatch:
798 		initMatchValue (d, &value[v->list.nValue], helper, doc, child);
799 	    default:
800 		break;
801 	    }
802 
803 	    v->list.value = value;
804 	    v->list.nValue++;
805 	}
806     }
807 }
808 
809 static char *
stringFromMetadataPathElement(CompMetadata * metadata,const char * path,const char * element)810 stringFromMetadataPathElement (CompMetadata *metadata,
811 			       const char   *path,
812 			       const char   *element)
813 {
814     char str[1024];
815 
816     snprintf (str, 1024, "%s/%s", path, element);
817 
818     return compGetStringFromMetadataPath (metadata, str);
819 }
820 
821 static Bool
boolFromMetadataPathElement(CompMetadata * metadata,const char * path,const char * element,Bool defaultValue)822 boolFromMetadataPathElement (CompMetadata *metadata,
823 			     const char   *path,
824 			     const char   *element,
825 			     Bool	  defaultValue)
826 {
827     Bool value = FALSE;
828     char *str;
829 
830     str = stringFromMetadataPathElement (metadata, path, element);
831     if (!str)
832 	return defaultValue;
833 
834     if (strcasecmp (str, "true") == 0)
835 	value = TRUE;
836 
837     free (str);
838 
839     return value;
840 }
841 
842 static void
initIntRestriction(CompMetadata * metadata,CompOptionRestriction * r,const char * path)843 initIntRestriction (CompMetadata	  *metadata,
844 		    CompOptionRestriction *r,
845 		    const char		  *path)
846 {
847     char *value;
848 
849     r->i.min = MINSHORT;
850     r->i.max = MAXSHORT;
851 
852     value = stringFromMetadataPathElement (metadata, path, "min");
853     if (value)
854     {
855 	r->i.min = strtol ((char *) value, NULL, 0);
856 	free (value);
857     }
858 
859     value = stringFromMetadataPathElement (metadata, path, "max");
860     if (value)
861     {
862 	r->i.max = strtol ((char *) value, NULL, 0);
863 	free (value);
864     }
865 }
866 
867 static void
initFloatRestriction(CompMetadata * metadata,CompOptionRestriction * r,const char * path)868 initFloatRestriction (CompMetadata	    *metadata,
869 		      CompOptionRestriction *r,
870 		      const char	    *path)
871 {
872     char *value;
873     char *loc;
874 
875     r->f.min	   = MINSHORT;
876     r->f.max	   = MAXSHORT;
877     r->f.precision = 0.1f;
878 
879     loc = setlocale (LC_NUMERIC, "C");
880     value = stringFromMetadataPathElement (metadata, path, "min");
881     if (value)
882     {
883 	r->f.min = strtod ((char *) value, NULL);
884 	free (value);
885     }
886 
887     value = stringFromMetadataPathElement (metadata, path, "max");
888     if (value)
889     {
890 	r->f.max = strtod ((char *) value, NULL);
891 	free (value);
892     }
893 
894     value = stringFromMetadataPathElement (metadata, path, "precision");
895     if (value)
896     {
897 	r->f.precision = strtod ((char *) value, NULL);
898 	free (value);
899     }
900 
901     setlocale (LC_NUMERIC, loc);
902 }
903 
904 static void
initActionState(CompMetadata * metadata,CompOptionType type,CompActionState * state,const char * path)905 initActionState (CompMetadata    *metadata,
906 		 CompOptionType  type,
907 		 CompActionState *state,
908 		 const char      *path)
909 {
910     static struct _StateMap {
911 	char	       *name;
912 	CompActionState state;
913     } map[] = {
914 	{ "key",     CompActionStateInitKey     },
915 	{ "button",  CompActionStateInitButton  },
916 	{ "bell",    CompActionStateInitBell    },
917 	{ "edge",    CompActionStateInitEdge    },
918 	{ "edgednd", CompActionStateInitEdgeDnd }
919     };
920     int	      i;
921     CompXPath xPath;
922     char      *grab;
923 
924     *state = CompActionStateAutoGrab;
925 
926     grab = stringFromMetadataPathElement (metadata, path, "passive_grab");
927     if (grab)
928     {
929 	if (strcmp (grab, "false") == 0)
930 	    *state = 0;
931 
932 	free (grab);
933     }
934 
935     if (type == CompOptionTypeEdge)
936     {
937 	char *noEdgeDelay;
938 
939 	noEdgeDelay = stringFromMetadataPathElement (metadata, path, "nodelay");
940 	if (noEdgeDelay)
941 	{
942 	    if (strcmp (noEdgeDelay, "true") == 0)
943 		*state |= CompActionStateNoEdgeDelay;
944 
945 	    free (noEdgeDelay);
946 	}
947     }
948 
949     if (!initXPathFromMetadataPathElement (&xPath, metadata, BAD_CAST path,
950 					   BAD_CAST "allowed"))
951 	return;
952 
953     for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
954     {
955 	xmlChar *value;
956 
957 	value = xmlGetProp (*xPath.obj->nodesetval->nodeTab,
958 			    BAD_CAST map[i].name);
959 	if (value)
960 	{
961 	    if (xmlStrcmp (value, BAD_CAST "true") == 0)
962 		*state |= map[i].state;
963 	    xmlFree (value);
964 	}
965     }
966 
967     finiXPath (&xPath);
968 }
969 
970 static Bool
initOptionFromMetadataPath(CompDisplay * d,CompMetadata * metadata,CompOption * option,const xmlChar * path)971 initOptionFromMetadataPath (CompDisplay   *d,
972 			    CompMetadata  *metadata,
973 			    CompOption	  *option,
974 			    const xmlChar *path)
975 {
976     CompXPath	    xPath, xDefaultPath;
977     xmlNodePtr	    node, defaultNode;
978     xmlDocPtr	    defaultDoc;
979     xmlChar	    *name, *type;
980     char	    *value;
981     CompActionState state = 0;
982     Bool	    helper = FALSE;
983 
984     if (!initXPathFromMetadataPath (&xPath, metadata, path))
985 	return FALSE;
986 
987     node = *xPath.obj->nodesetval->nodeTab;
988 
989     type = xmlGetProp (node, BAD_CAST "type");
990     if (type)
991     {
992 	option->type = getOptionType ((char *) type);
993 	xmlFree (type);
994     }
995     else
996     {
997 	option->type = CompOptionTypeBool;
998     }
999 
1000     name = xmlGetProp (node, BAD_CAST "name");
1001     option->name = strdup ((char *) name);
1002     xmlFree (name);
1003 
1004     if (initXPathFromMetadataPathElement (&xDefaultPath, metadata, path,
1005 					  BAD_CAST "default"))
1006     {
1007 	defaultDoc  = xDefaultPath.doc;
1008 	defaultNode = *xDefaultPath.obj->nodesetval->nodeTab;
1009     }
1010     else
1011     {
1012 	defaultDoc  = NULL;
1013 	defaultNode = NULL;
1014     }
1015 
1016     switch (option->type) {
1017     case CompOptionTypeBool:
1018 	initBoolValue (&option->value, defaultDoc, defaultNode);
1019 	break;
1020     case CompOptionTypeInt:
1021 	initIntRestriction (metadata, &option->rest, (char *) path);
1022 	initIntValue (&option->value, &option->rest, defaultDoc, defaultNode);
1023 	break;
1024     case CompOptionTypeFloat:
1025 	initFloatRestriction (metadata, &option->rest, (char *) path);
1026 	initFloatValue (&option->value, &option->rest, defaultDoc, defaultNode);
1027 	break;
1028     case CompOptionTypeString:
1029 	initStringValue (&option->value, &option->rest,
1030 			 defaultDoc, defaultNode);
1031 	break;
1032     case CompOptionTypeColor:
1033 	initColorValue (&option->value, defaultDoc, defaultNode);
1034 	break;
1035     case CompOptionTypeAction:
1036 	initActionState (metadata, option->type, &state, (char *) path);
1037 	initActionValue (d, &option->value, state, defaultDoc, defaultNode);
1038 	break;
1039     case CompOptionTypeKey:
1040 	initActionState (metadata, option->type, &state, (char *) path);
1041 	initKeyValue (d, &option->value, state, defaultDoc, defaultNode);
1042 	break;
1043     case CompOptionTypeButton:
1044 	initActionState (metadata, option->type, &state, (char *) path);
1045 	initButtonValue (d, &option->value, state, defaultDoc, defaultNode);
1046 	break;
1047     case CompOptionTypeEdge:
1048 	initActionState (metadata, option->type, &state, (char *) path);
1049 	initEdgeValue (d, &option->value, state, defaultDoc, defaultNode);
1050 	break;
1051     case CompOptionTypeBell:
1052 	initActionState (metadata, option->type, &state, (char *) path);
1053 	initBellValue (d, &option->value, state, defaultDoc, defaultNode);
1054 	break;
1055     case CompOptionTypeMatch:
1056 	helper = boolFromMetadataPathElement (metadata, (char *) path, "helper",
1057 					      FALSE);
1058 	initMatchValue (d, &option->value, helper, defaultDoc, defaultNode);
1059 	break;
1060     case CompOptionTypeList:
1061 	value = stringFromMetadataPathElement (metadata, (char *) path, "type");
1062 	if (value)
1063 	{
1064 	    option->value.list.type = getOptionType ((char *) value);
1065 	    free (value);
1066 	}
1067 	else
1068 	{
1069 	    option->value.list.type = CompOptionTypeBool;
1070 	}
1071 
1072 	switch (option->value.list.type) {
1073 	case CompOptionTypeInt:
1074 	    initIntRestriction (metadata, &option->rest, (char *) path);
1075 	    break;
1076 	case CompOptionTypeFloat:
1077 	    initFloatRestriction (metadata, &option->rest, (char *) path);
1078 	    break;
1079 	case CompOptionTypeAction:
1080 	case CompOptionTypeKey:
1081 	case CompOptionTypeButton:
1082 	case CompOptionTypeEdge:
1083 	case CompOptionTypeBell:
1084 	    initActionState (metadata, option->value.list.type,
1085 			     &state, (char *) path);
1086 	    break;
1087 	case CompOptionTypeMatch:
1088 	    helper = boolFromMetadataPathElement (metadata, (char *) path,
1089 						  "helper", FALSE);
1090 	default:
1091 	    break;
1092 	}
1093 
1094 	initListValue (d, &option->value, &option->rest, state, helper,
1095 		       defaultDoc, defaultNode);
1096 	break;
1097     }
1098 
1099     if (defaultDoc)
1100 	finiXPath (&xDefaultPath);
1101 
1102     finiXPath (&xPath);
1103 
1104     return TRUE;
1105 }
1106 
1107 Bool
compInitScreenOptionFromMetadata(CompScreen * s,CompMetadata * m,CompOption * o,const char * name)1108 compInitScreenOptionFromMetadata (CompScreen   *s,
1109 				  CompMetadata *m,
1110 				  CompOption   *o,
1111 				  const char   *name)
1112 {
1113     char str[1024];
1114 
1115     sprintf (str, "/compiz/%s/screen//option[@name=\"%s\"]", m->path, name);
1116 
1117     return initOptionFromMetadataPath (s->display, m, o, BAD_CAST str);
1118 }
1119 
1120 static void
finiScreenOptionValue(CompScreen * s,CompOptionValue * v,CompOptionType type)1121 finiScreenOptionValue (CompScreen      *s,
1122 		       CompOptionValue *v,
1123 		       CompOptionType  type)
1124 {
1125     int	i;
1126 
1127     switch (type) {
1128     case CompOptionTypeAction:
1129     case CompOptionTypeKey:
1130     case CompOptionTypeButton:
1131     case CompOptionTypeEdge:
1132     case CompOptionTypeBell:
1133 	if (v->action.state & CompActionStateAutoGrab)
1134 	    removeScreenAction (s, &v->action);
1135 	break;
1136     case CompOptionTypeList:
1137 	for (i = 0; i < v->list.nValue; i++)
1138 	    finiScreenOptionValue (s, &v->list.value[i], v->list.type);
1139     default:
1140 	break;
1141     }
1142 }
1143 
1144 void
compFiniScreenOption(CompScreen * s,CompOption * o)1145 compFiniScreenOption (CompScreen *s,
1146 		      CompOption *o)
1147 {
1148     finiScreenOptionValue (s, &o->value, o->type);
1149     compFiniOption (o);
1150     free (o->name);
1151 }
1152 
1153 Bool
compInitScreenOptionsFromMetadata(CompScreen * s,CompMetadata * m,const CompMetadataOptionInfo * info,CompOption * opt,int n)1154 compInitScreenOptionsFromMetadata (CompScreen			*s,
1155 				   CompMetadata			*m,
1156 				   const CompMetadataOptionInfo *info,
1157 				   CompOption			*opt,
1158 				   int				n)
1159 {
1160     int i;
1161 
1162     for (i = 0; i < n; i++)
1163     {
1164 	if (!compInitScreenOptionFromMetadata (s, m, &opt[i], info[i].name))
1165 	{
1166 	    compFiniScreenOptions (s, opt, i);
1167 	    return FALSE;
1168 	}
1169 
1170 	if (info[i].initiate)
1171 	    opt[i].value.action.initiate = info[i].initiate;
1172 
1173 	if (info[i].terminate)
1174 	    opt[i].value.action.terminate = info[i].terminate;
1175     }
1176 
1177     return TRUE;
1178 }
1179 
1180 void
compFiniScreenOptions(CompScreen * s,CompOption * opt,int n)1181 compFiniScreenOptions (CompScreen *s,
1182 		       CompOption *opt,
1183 		       int	  n)
1184 {
1185     int i;
1186 
1187     for (i = 0; i < n; i++)
1188 	compFiniScreenOption (s, &opt[i]);
1189 }
1190 
1191 Bool
compSetScreenOption(CompScreen * s,CompOption * o,CompOptionValue * value)1192 compSetScreenOption (CompScreen      *s,
1193 		     CompOption      *o,
1194 		     CompOptionValue *value)
1195 {
1196     if (compSetOption (o, value))
1197 	return TRUE;
1198 
1199     return FALSE;
1200 }
1201 
1202 Bool
compInitDisplayOptionFromMetadata(CompDisplay * d,CompMetadata * m,CompOption * o,const char * name)1203 compInitDisplayOptionFromMetadata (CompDisplay  *d,
1204 				   CompMetadata *m,
1205 				   CompOption	*o,
1206 				   const char	*name)
1207 {
1208     char str[1024];
1209 
1210     sprintf (str, "/compiz/%s/display//option[@name=\"%s\"]", m->path, name);
1211 
1212     return initOptionFromMetadataPath (d, m, o, BAD_CAST str);
1213 }
1214 
1215 static void
finiDisplayOptionValue(CompDisplay * d,CompOptionValue * v,CompOptionType type)1216 finiDisplayOptionValue (CompDisplay	*d,
1217 			CompOptionValue *v,
1218 			CompOptionType  type)
1219 {
1220     CompScreen *s;
1221     int	       i;
1222 
1223     switch (type) {
1224     case CompOptionTypeAction:
1225     case CompOptionTypeKey:
1226     case CompOptionTypeButton:
1227     case CompOptionTypeEdge:
1228     case CompOptionTypeBell:
1229 	if (v->action.state & CompActionStateAutoGrab)
1230 	    for (s = d->screens; s; s = s->next)
1231 		removeScreenAction (s, &v->action);
1232 	break;
1233     case CompOptionTypeList:
1234 	for (i = 0; i < v->list.nValue; i++)
1235 	    finiDisplayOptionValue (d, &v->list.value[i], v->list.type);
1236     default:
1237 	break;
1238     }
1239 }
1240 
1241 void
compFiniDisplayOption(CompDisplay * d,CompOption * o)1242 compFiniDisplayOption (CompDisplay *d,
1243 		       CompOption  *o)
1244 {
1245     finiDisplayOptionValue (d, &o->value, o->type);
1246     compFiniOption (o);
1247     free (o->name);
1248 }
1249 
1250 Bool
compInitDisplayOptionsFromMetadata(CompDisplay * d,CompMetadata * m,const CompMetadataOptionInfo * info,CompOption * opt,int n)1251 compInitDisplayOptionsFromMetadata (CompDisplay			 *d,
1252 				    CompMetadata		 *m,
1253 				    const CompMetadataOptionInfo *info,
1254 				    CompOption			 *opt,
1255 				    int				 n)
1256 {
1257     int i;
1258 
1259     for (i = 0; i < n; i++)
1260     {
1261 	if (!compInitDisplayOptionFromMetadata (d, m, &opt[i], info[i].name))
1262 	{
1263 	    compFiniDisplayOptions (d, opt, i);
1264 	    return FALSE;
1265 	}
1266 
1267 	if (info[i].initiate)
1268 	    opt[i].value.action.initiate = info[i].initiate;
1269 
1270 	if (info[i].terminate)
1271 	    opt[i].value.action.terminate = info[i].terminate;
1272     }
1273 
1274     return TRUE;
1275 }
1276 
1277 void
compFiniDisplayOptions(CompDisplay * d,CompOption * opt,int n)1278 compFiniDisplayOptions (CompDisplay *d,
1279 			CompOption  *opt,
1280 			int	    n)
1281 {
1282     int i;
1283 
1284     for (i = 0; i < n; i++)
1285 	compFiniDisplayOption (d, &opt[i]);
1286 }
1287 
1288 Bool
compSetDisplayOption(CompDisplay * d,CompOption * o,CompOptionValue * value)1289 compSetDisplayOption (CompDisplay     *d,
1290 		      CompOption      *o,
1291 		      CompOptionValue *value)
1292 {
1293     if (isActionOption (o))
1294     {
1295 	if (o->value.action.state & CompActionStateAutoGrab)
1296 	{
1297 	    if (setDisplayAction (d, o, value))
1298 		return TRUE;
1299 	}
1300 	else
1301 	{
1302 	    if (compSetActionOption (o, value))
1303 		return TRUE;
1304 	}
1305     }
1306     else
1307     {
1308 	if (compSetOption (o, value))
1309 	    return TRUE;
1310     }
1311 
1312     return FALSE;
1313 }
1314 
1315 char *
compGetStringFromMetadataPath(CompMetadata * metadata,const char * path)1316 compGetStringFromMetadataPath (CompMetadata *metadata,
1317 			       const char   *path)
1318 {
1319     CompXPath xPath;
1320     char      *v = NULL;
1321 
1322     if (!initXPathFromMetadataPath (&xPath, metadata, BAD_CAST path))
1323 	return NULL;
1324 
1325     xPath.obj = xmlXPathConvertString (xPath.obj);
1326 
1327     if (xPath.obj->type == XPATH_STRING && xPath.obj->stringval)
1328 	v = strdup ((char *) xPath.obj->stringval);
1329 
1330     finiXPath (&xPath);
1331 
1332     return v;
1333 }
1334 
1335 char *
compGetShortPluginDescription(CompMetadata * m)1336 compGetShortPluginDescription (CompMetadata *m)
1337 {
1338     char str[1024];
1339 
1340     sprintf (str, "/compiz/%s/short/child::text()", m->path);
1341 
1342     return compGetStringFromMetadataPath (m, str);
1343 }
1344 
1345 char *
compGetLongPluginDescription(CompMetadata * m)1346 compGetLongPluginDescription (CompMetadata *m)
1347 {
1348     char str[1024];
1349 
1350     sprintf (str, "/compiz/%s/long/child::text()", m->path);
1351 
1352     return compGetStringFromMetadataPath (m, str);
1353 }
1354 
1355 char *
compGetShortScreenOptionDescription(CompMetadata * m,CompOption * o)1356 compGetShortScreenOptionDescription (CompMetadata *m,
1357 				     CompOption   *o)
1358 {
1359     char str[1024];
1360 
1361     sprintf (str, "/compiz/%s/screen//option[@name=\"%s\"]/short/child::text()",
1362 	     m->path, o->name);
1363 
1364     return compGetStringFromMetadataPath (m, str);
1365 }
1366 
1367 char *
compGetLongScreenOptionDescription(CompMetadata * m,CompOption * o)1368 compGetLongScreenOptionDescription (CompMetadata *m,
1369 				    CompOption   *o)
1370 {
1371     char str[1024];
1372 
1373     sprintf (str, "/compiz/%s/screen//option[@name=\"%s\"]/long/child::text()",
1374 	     m->path, o->name);
1375 
1376     return compGetStringFromMetadataPath (m, str);
1377 }
1378 
1379 
1380 char *
compGetShortDisplayOptionDescription(CompMetadata * m,CompOption * o)1381 compGetShortDisplayOptionDescription (CompMetadata *m,
1382 				      CompOption   *o)
1383 {
1384     char str[1024];
1385 
1386     sprintf (str,
1387 	     "/compiz/%s/display//option[@name=\"%s\"]/short/child::text()",
1388 	     m->path, o->name);
1389 
1390     return compGetStringFromMetadataPath (m, str);
1391 }
1392 
1393 
1394 char *
compGetLongDisplayOptionDescription(CompMetadata * m,CompOption * o)1395 compGetLongDisplayOptionDescription (CompMetadata *m,
1396 				     CompOption   *o)
1397 {
1398     char str[1024];
1399 
1400     sprintf (str, "/compiz/%s/display//option[@name=\"%s\"]/long/child::text()",
1401 	     m->path, o->name);
1402 
1403     return compGetStringFromMetadataPath (m, str);
1404 }
1405 
1406 int
compReadXmlChunk(const char * src,int * offset,char * buffer,int length)1407 compReadXmlChunk (const char *src,
1408 		  int	     *offset,
1409 		  char	     *buffer,
1410 		  int	     length)
1411 {
1412     int srcLength = strlen (src);
1413     int srcOffset = *offset;
1414 
1415     if (srcOffset > srcLength)
1416 	srcOffset = srcLength;
1417 
1418     *offset -= srcOffset;
1419 
1420     src += srcOffset;
1421     srcLength -= srcOffset;
1422 
1423     if (srcLength > 0 && length > 0)
1424     {
1425 	if (srcLength < length)
1426 	    length = srcLength;
1427 
1428 	memcpy (buffer, src, length);
1429 
1430 	return length;
1431     }
1432 
1433     return 0;
1434 }
1435 
1436 int
compReadXmlChunkFromMetadataOptionInfo(const CompMetadataOptionInfo * info,int * offset,char * buffer,int length)1437 compReadXmlChunkFromMetadataOptionInfo (const CompMetadataOptionInfo *info,
1438 					int			     *offset,
1439 					char			     *buffer,
1440 					int			     length)
1441 {
1442     int i;
1443 
1444     i = compReadXmlChunk ("<option name=\"", offset, buffer, length);
1445     i += compReadXmlChunk (info->name, offset, buffer + i, length - i);
1446 
1447     if (info->type)
1448     {
1449 	i += compReadXmlChunk ("\" type=\"", offset, buffer + i, length - i);
1450 	i += compReadXmlChunk (info->type, offset, buffer + i, length - i);
1451     }
1452 
1453     if (info->data)
1454     {
1455 	i += compReadXmlChunk ("\">", offset, buffer + i, length - i);
1456 	i += compReadXmlChunk (info->data, offset, buffer + i, length - i);
1457 	i += compReadXmlChunk ("</option>", offset, buffer + i, length - i);
1458     }
1459     else
1460     {
1461 	i += compReadXmlChunk ("\"/>", offset, buffer + i, length - i);
1462     }
1463 
1464     return i;
1465 }
1466