Modifying version number for building on tizen 3.0
[platform/upstream/iotivity.git] / service / protocol-plugin / lib / cpluff / libcpluff / ploader.c
1 /*-------------------------------------------------------------------------
2  * C-Pluff, a plug-in framework for C
3  * Copyright 2007 Johannes Lehtinen
4  * 
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  *-----------------------------------------------------------------------*/
23
24 /** @file
25  * Plug-in descriptor loader
26  */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <assert.h>
32 #include <stdarg.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <expat.h>
36 #include "cpluff.h"
37 #include "defines.h"
38 #include "util.h"
39 #include "internal.h"
40
41 // Use XMLCALL if available
42 #ifdef XMLCALL
43 #define CP_XMLCALL XMLCALL
44 #else
45 #define CP_XMLCALL
46 #endif
47
48
49 /* ------------------------------------------------------------------------
50  * Constants
51  * ----------------------------------------------------------------------*/
52
53 /// XML parser buffer size (in bytes) 
54 #define CP_XML_PARSER_BUFFER_SIZE 4096
55
56 /// Initial configuration element value size 
57 #define CP_CFG_ELEMENT_VALUE_INITSIZE 64
58
59 /// Plugin descriptor name 
60 #define CP_PLUGIN_DESCRIPTOR "plugin.xml"
61
62
63 /* ------------------------------------------------------------------------
64  * Internal data types
65  * ----------------------------------------------------------------------*/
66
67 typedef struct ploader_context_t ploader_context_t;
68
69 /// Parser states 
70 typedef enum parser_state_t {
71         PARSER_BEGIN,
72         PARSER_PLUGIN,
73         PARSER_REQUIRES,
74         PARSER_EXTENSION,
75         PARSER_END,
76         PARSER_UNKNOWN,
77         PARSER_ERROR
78 } parser_state_t;
79
80 /// Plug-in loader context 
81 struct ploader_context_t {
82
83         /// The plug-in context, or NULL if none
84         cp_context_t *context;
85
86         /// The XML parser being used 
87         XML_Parser parser;
88         
89         /// The file being parsed 
90         char *file;
91         
92         /// The plug-in being constructed 
93         cp_plugin_info_t *plugin;
94         
95         /// The configuration element being constructed 
96         cp_cfg_element_t *configuration;
97         
98         /// The current parser state 
99         parser_state_t state;
100         
101         /// The saved parser state (used in PARSER_UNKNOWN) 
102         parser_state_t saved_state;
103         
104         /**
105          * The current parser depth (used in PARSER_UNKNOWN and PARSER_EXTENSION)
106          */
107         unsigned int depth;
108         
109         /// The number of skipped configuration elements 
110         unsigned int skippedCEs;
111
112         /// Size of allocated imports table 
113         size_t imports_size;
114         
115         /// Size of allocated extension points table 
116         size_t ext_points_size;
117         
118         /// Size of allocated extensions table 
119         size_t extensions_size;
120         
121         /// Buffer for a value being read 
122         char *value;
123         
124         /// Size of allocated value field 
125         size_t value_size;
126         
127         /// Current length of value string 
128         size_t value_length;
129         
130         /// The number of parsing errors that have occurred 
131         unsigned int error_count;
132         
133         /// The number of resource errors that have occurred 
134         unsigned int resource_error_count;
135 };
136
137
138 /* ------------------------------------------------------------------------
139  * Function definitions
140  * ----------------------------------------------------------------------*/
141
142 /**
143  * Reports a descriptor error. Does not set the parser to error state but
144  * increments the error count, unless this is merely a warning.
145  * 
146  * @param context the parsing context
147  * @param warn whether this is only a warning
148  * @param error_msg the error message
149  * @param ... parameters for the error message
150  */
151 static void descriptor_errorf(ploader_context_t *plcontext, int warn,
152         const char *error_msg, ...) {
153         va_list ap;
154         char message[128];
155
156         va_start(ap, error_msg);
157         vsnprintf(message, sizeof(message), error_msg, ap);
158         va_end(ap);
159         message[127] = '\0';
160         if (warn) {
161                 cpi_warnf(plcontext->context,
162                         N_("Suspicious plug-in descriptor content in %s, line %d, column %d (%s)."),
163                 plcontext->file,
164                 XML_GetCurrentLineNumber(plcontext->parser),
165                 XML_GetCurrentColumnNumber(plcontext->parser) + 1,
166                 message);
167         } else {                                
168                 cpi_errorf(plcontext->context,
169                         N_("Invalid plug-in descriptor content in %s, line %d, column %d (%s)."),
170                         plcontext->file,
171                         XML_GetCurrentLineNumber(plcontext->parser),
172                         XML_GetCurrentColumnNumber(plcontext->parser) + 1,
173                         message);
174         }
175         if (!warn) {
176                 plcontext->error_count++;
177         }
178 }
179
180 /**
181  * Reports insufficient system resources while parsing and increments the
182  * resource error count.
183  * 
184  * @param context the parsing context
185  */
186 static void resource_error(ploader_context_t *plcontext) {
187         if (plcontext->resource_error_count == 0) {
188                 cpi_errorf(plcontext->context,
189                         N_("Insufficient system resources to parse plug-in descriptor content in %s, line %d, column %d."),
190                         plcontext->file,
191                         XML_GetCurrentLineNumber(plcontext->parser),
192                         XML_GetCurrentColumnNumber(plcontext->parser) + 1);
193         }
194         plcontext->resource_error_count++;
195 }
196
197 /**
198  * Returns whether the specified NULL-terminated list of strings includes
199  * the specified string.
200  * 
201  * @param list the NULL-terminated list of strings, or NULL if none
202  * @param str the string
203  * @param step the stepping (1 to check every string or 2 to check every
204  *                      other string)
205  * @return pointer to the location of the string or NULL if not found
206  */
207 static const XML_Char * const *contains_str(const XML_Char * const *list,
208         const XML_Char *str, int step) {
209         if (list != NULL) {
210                 while (*list != NULL) {
211                         if (!strcmp(*list, str)) {
212                                 return list;
213                         }
214                         list += step;
215                 }
216         }
217         return NULL;
218 }
219
220 /**
221  * Checks that an element has non-empty values for required attributes.
222  * Increments the error count for each missing attribute.
223  * 
224  * @param context the parsing context
225  * @param elem the element being checked
226  * @param atts the attribute list for the element
227  * @param req_atts the required attributes (NULL terminated list, or NULL)
228  * @return whether the required attributes are present
229  */
230 static int check_req_attributes(ploader_context_t *plcontext,
231         const XML_Char *elem, const XML_Char * const *atts,
232         const XML_Char * const *req_atts) {
233         const XML_Char * const *a;
234         int error = 0;
235         
236         // Check that required attributes have non-empty values 
237         for (a = req_atts; a != NULL && *a != NULL; a++) {
238                 const XML_Char * const *av;
239                 
240                 if ((av = contains_str(atts, *a, 2)) != NULL) {
241                         if ((*(av + 1))[0] == '\0') {
242                                 descriptor_errorf(plcontext, 0,
243                                         _("required attribute %s for element %s has an empty value"),
244                                         *a, elem);
245                                 error = 1;
246                         }
247                 } else {
248                         descriptor_errorf(plcontext, 0,
249                                 _("required attribute %s missing for element %s"),
250                                 *a, elem);
251                         error = 1;
252                 }
253         }
254         
255         return !error;
256 }
257
258 /**
259  * Checks that an element has non-empty values for required attributes and
260  * warns if there are unknown attributes. Increments the error count for
261  * each missing required attribute.
262  * 
263  * @param context the parsing context
264  * @param elem the element being checked
265  * @param atts the attribute list for the element
266  * @param req_atts the required attributes (NULL terminated list, or NULL)
267  * @param opt_atts the optional attributes (NULL terminated list, or NULL)
268  * @return whether the required attributes are present
269  */
270 static int check_attributes(ploader_context_t *plcontext,
271         const XML_Char *elem, const XML_Char * const *atts,
272         const XML_Char * const *req_atts, const XML_Char * const *opt_atts) {
273         int error = 0;
274         
275         // Check required attributes
276         error = !check_req_attributes(plcontext, elem, atts, req_atts);
277         
278         // Warn if there are unknown attributes 
279         for (; *atts != NULL; atts += 2) {
280                 if (contains_str(req_atts, *atts, 1) == NULL
281                         && contains_str(opt_atts, *atts, 1) == NULL) {
282                         descriptor_errorf(plcontext, 1,
283                                 _("ignoring unknown attribute %s for element %s"),
284                                 *atts, elem);
285                 }
286         }
287         
288         return !error;
289 }
290
291 /**
292  * Allocates memory using malloc. Reports a resource error if there is not
293  * enough available memory.
294  * 
295  * @param context the parsing context
296  * @param size the number of bytes to allocate
297  * @return pointer to the allocated memory, or NULL if memory allocation failed
298  */
299 static void *parser_malloc(ploader_context_t *plcontext, size_t size) {
300         void *ptr;
301
302         if ((ptr = malloc(size)) == NULL) {
303                 resource_error(plcontext);
304         }
305         return ptr;
306 }
307
308 /**
309  * Makes a copy of the specified string. The memory is allocated using malloc.
310  * Reports a resource error if there is not enough available memory.
311  * 
312  * @param context the parsing context
313  * @param src the source string to be copied
314  * @return copy of the string, or NULL if memory allocation failed
315  */
316 static char *parser_strdup(ploader_context_t *plcontext, const char *src) {
317         char *dup;
318
319         if ((dup = strdup(src)) == NULL) {
320                 resource_error(plcontext);
321         }
322         return dup;
323 }
324
325 /**
326  * Concatenates the specified strings into a new string. The memory for the concatenated
327  * string is allocated using malloc. Reports a resource error if there is not
328  * enough available memory.
329  * 
330  * @param context the parsing context
331  * @param ... the strings to be concatenated, terminated by NULL
332  * @return the concatenated string, or NULL if memory allocation failed
333  */
334 static char *parser_strscat(ploader_context_t *plcontext, ...) {
335         va_list ap;
336         const char *str;
337         char *dst;
338         size_t len;
339         
340         // Calculate the length of the concatenated string 
341         va_start(ap, plcontext);
342         len = 0;
343         while ((str = va_arg(ap, const char *)) != NULL) {
344                 len += strlen(str);
345         }
346         va_end(ap);
347         
348         // Allocate space for the concatenated string 
349         if ((dst = parser_malloc(plcontext, sizeof(char) * (len + 1))) == NULL) {
350                 return NULL;
351         }
352         
353         // Copy the strings 
354         len = 0;
355         va_start(ap, plcontext);
356         while ((str = va_arg(ap, const char *)) != NULL) {
357                 strcpy(dst + len, str);
358                 len += strlen(str);
359         }
360         va_end(ap);
361         dst[len] = '\0';
362         return dst;
363 }
364
365 /**
366  * Puts the parser to a state in which it skips an unknown element.
367  * Warns error handlers about the unknown element.
368  * 
369  * @param context the parsing context
370  * @param elem the element name
371  */
372 static void unexpected_element(ploader_context_t *plcontext, const XML_Char *elem) {
373         plcontext->saved_state = plcontext->state;
374         plcontext->state = PARSER_UNKNOWN;
375         plcontext->depth = 0;
376         descriptor_errorf(plcontext, 1, _("ignoring unexpected element %s and its contents"), elem);
377 }
378
379 /**
380  * Creates a copy of the specified attributes. Reports failed memory
381  * allocation.
382  * 
383  * @param context the parser context
384  * @param src the source attributes to be copied
385  * @param num pointer to the location where number of attributes is stored,
386  *                      or NULL for none
387  * @return the duplicated attribute array, or NULL if empty or failed
388  */
389 static char **parser_attsdup(ploader_context_t *plcontext, const XML_Char * const *src,
390         unsigned int *num_atts) {
391         char **atts = NULL, *attr_data = NULL;
392         unsigned int i;
393         unsigned int num;
394         size_t attr_size;
395         
396         // Calculate the number of attributes and the amount of space required 
397         for (num = 0, attr_size = 0; src[num] != NULL; num++) {
398                 attr_size += strlen(src[num]) + 1;
399         }
400         assert((num & 1) == 0);
401         
402         // Allocate necessary memory and copy attribute data 
403         if (num > 0) {
404                 if ((atts = parser_malloc(plcontext, num * sizeof(char *))) != NULL) {
405                         if ((attr_data = parser_malloc(plcontext, attr_size * sizeof(char))) != NULL) {
406                                 size_t offset;
407                         
408                                 for (i = 0, offset = 0; i < num; i++) {
409                                         strcpy(attr_data + offset, src[i]);
410                                         atts[i] = attr_data + offset;
411                                         offset += strlen(src[i]) + 1;
412                                 }
413                         }
414                 }
415         }
416         
417         // If successful then return duplicates, otherwise free any allocations 
418         if (num == 0 || (atts != NULL && attr_data != NULL)) {
419                 if (num_atts != NULL) {
420                         *num_atts = num / 2;
421                 }
422                 return atts;
423         } else {
424                 free(attr_data);
425                 free(atts);
426                 return NULL;
427         }
428 }
429
430 /**
431  * Initializes a configuration element. Reports an error if memory allocation fails.
432  * 
433  * @param context the parser context
434  * @param ce the configuration element to be initialized
435  * @param name the element name
436  * @param atts the element attributes
437  * @param parent the parent element
438  */
439 static void init_cfg_element(ploader_context_t *plcontext, cp_cfg_element_t *ce,
440         const XML_Char *name, const XML_Char * const *atts, cp_cfg_element_t *parent) {
441         
442         // Initialize the configuration element 
443         memset(ce, 0, sizeof(cp_cfg_element_t));
444         ce->name = parser_strdup(plcontext, name);
445         ce->atts = parser_attsdup(plcontext, atts, &(ce->num_atts));
446         ce->value = NULL;
447         plcontext->value = NULL;
448         plcontext->value_size = 0;
449         plcontext->value_length = 0;
450         ce->parent = parent;
451         ce->children = NULL;    
452 }
453
454 /**
455  * Processes the character data while parsing.
456  * 
457  * @param userData the parsing context
458  * @param str the string data
459  * @param len the string length
460  */
461 static void CP_XMLCALL character_data_handler(
462         void *userData, const XML_Char *str, int len) {
463         ploader_context_t *plcontext = userData;
464         
465         // Ignore leading whitespace 
466         if (plcontext->value == NULL) {
467                 int i;
468                 
469                 for (i = 0; i < len; i++) {
470                         if (str[i] != ' ' && str[i] != '\n' && str[i] != '\r' && str[i] != '\t') {
471                                 break;
472                         }
473                 }
474                 str += i;
475                 len -= i;
476                 if (len == 0) {
477                         return;
478                 }
479         }
480         
481         // Allocate more memory for the character data if needed 
482         if (plcontext->value_length + len >= plcontext->value_size) {
483                 size_t ns;
484                 char *nv;
485
486                 ns = plcontext->value_size;
487                 while (plcontext->value_length + len >= ns) {           
488                         if (ns == 0) {
489                                 ns = CP_CFG_ELEMENT_VALUE_INITSIZE;
490                         } else {
491                                 ns = 2 * ns;
492                         }
493                 }
494                 if ((nv = realloc(plcontext->value, ns * sizeof(char))) != NULL) {
495                         plcontext->value = nv;
496                         plcontext->value_size = ns;
497                 } else {
498                         resource_error(plcontext);
499                         return;
500                 }
501         }
502         
503         // Copy character data 
504         strncpy(plcontext->value + plcontext->value_length, str, len * sizeof(char));
505         plcontext->value_length += len;
506 }
507
508 /**
509  * Processes the start of element events while parsing.
510  * 
511  * @param userData the parsing context
512  * @param name the element name
513  * @param atts the element attributes
514  */
515 static void CP_XMLCALL start_element_handler(
516         void *userData, const XML_Char *name, const XML_Char **atts) {
517         static const XML_Char * const req_plugin_atts[] = { "id", NULL };
518         static const XML_Char * const opt_plugin_atts[] = { "name", "version", "provider-name", NULL };
519         static const XML_Char * const req_bwcompatibility_atts[] = { NULL };
520         static const XML_Char * const opt_bwcompatibility_atts[] = { "abi", "api", NULL };
521         static const XML_Char * const req_cpluff_atts[] = { "version", NULL };
522         static const XML_Char * const opt_cpluff_atts[] = { NULL };
523         static const XML_Char * const req_import_atts[] = { "plugin", NULL };
524         static const XML_Char * const opt_import_atts[] = { "version", "optional", NULL };
525         static const XML_Char * const req_runtime_atts[] = { "library", NULL };
526         static const XML_Char * const opt_runtime_atts[] = { "funcs", NULL };
527         static const XML_Char * const req_ext_point_atts[] = { "id", NULL };
528         static const XML_Char * const opt_ext_point_atts[] = { "name", "schema", NULL };
529         static const XML_Char * const req_extension_atts[] = { "point", NULL };
530         //static const XML_Char * const opt_extension_atts[] = { "id", "name", NULL };
531         ploader_context_t *plcontext = userData;
532         unsigned int i;
533
534         // Process element start 
535         switch (plcontext->state) {
536
537                 case PARSER_BEGIN:
538                         if (!strcmp(name, "plugin")) {
539                                 plcontext->state = PARSER_PLUGIN;
540                                 if (!check_attributes(plcontext, name, atts,
541                                                 req_plugin_atts, opt_plugin_atts)) {
542                                         break;
543                                 }
544                                 for (i = 0; atts[i] != NULL; i += 2) {
545                                         if (!strcmp(atts[i], "name")) {
546                                                 plcontext->plugin->name
547                                                         = parser_strdup(plcontext, atts[i+1]);
548                                         } else if (!strcmp(atts[i], "id")) {
549                                                 plcontext->plugin->identifier
550                                                         = parser_strdup(plcontext, atts[i+1]);
551                                         } else if (!strcmp(atts[i], "version")) {
552                                                 plcontext->plugin->version
553                                                         = parser_strdup(plcontext, atts[i+1]);
554                                         } else if (!strcmp(atts[i], "provider-name")) {
555                                                 plcontext->plugin->provider_name
556                                                         = parser_strdup(plcontext, atts[i+1]);
557                                         } else if(!strcmp(atts[i],"url")){
558                                                 plcontext->plugin->url = parser_strdup(plcontext, atts[i+1]);
559                                         } else if(!strcmp(atts[i],"resourcetype")){
560                                                 plcontext->plugin->resourcetype = parser_strdup(plcontext, atts[i+1]);
561                                         }
562                                 }
563                         } else {
564                                 unexpected_element(plcontext, name);
565                         }
566                         break;
567
568                 case PARSER_PLUGIN:
569                         if (!strcmp(name, "backwards-compatibility")) {
570                                 if (check_attributes(plcontext, name, atts,
571                                                 req_bwcompatibility_atts, opt_bwcompatibility_atts)) {
572                                         for (i = 0; atts[i] != NULL; i += 2) {
573                                                 if (!strcmp(atts[i], "abi")) {
574                                                         plcontext->plugin->abi_bw_compatibility = parser_strdup(plcontext, atts[i+1]);
575                                                 } else if (!strcmp(atts[i], "api")) {
576                                                         plcontext->plugin->api_bw_compatibility = parser_strdup(plcontext, atts[i+1]);
577                                                 }
578                                         }
579                                 }
580                         } else if (!strcmp(name, "requires")) {
581                                 plcontext->state = PARSER_REQUIRES;
582                         } else if (!strcmp(name, "runtime")) {
583                                 if (check_attributes(plcontext, name, atts,
584                                                 req_runtime_atts, opt_runtime_atts)) {
585                                         for (i = 0; atts[i] != NULL; i += 2) {
586                                                 if (!strcmp(atts[i], "library")) {
587                                                         plcontext->plugin->runtime_lib_name
588                                                                 = parser_strdup(plcontext, atts[i+1]);
589                                                 } else if (!strcmp(atts[i], "funcs")) {
590                                                         plcontext->plugin->runtime_funcs_symbol
591                                                                 = parser_strdup(plcontext, atts[i+1]);
592                                                 }
593                                         }
594                                 }
595                         } else if (!strcmp(name, "extension-point")) {
596                                 if (check_attributes(plcontext, name, atts,
597                                                 req_ext_point_atts, opt_ext_point_atts)) {
598                                         cp_ext_point_t *ext_point;
599                                         
600                                         // Allocate space for extension points, if necessary 
601                                         if (plcontext->plugin->num_ext_points == plcontext->ext_points_size) {
602                                                 cp_ext_point_t *nep;
603                                                 size_t ns;
604                                                 
605                                                 if (plcontext->ext_points_size == 0) {
606                                                         ns = 4;
607                                                 } else {
608                                                         ns = plcontext->ext_points_size * 2;
609                                                 }
610                                                 if ((nep = realloc(plcontext->plugin->ext_points,
611                                                                 ns * sizeof(cp_ext_point_t))) == NULL) {
612                                                         resource_error(plcontext);
613                                                         break;
614                                                 }
615                                                 plcontext->plugin->ext_points = nep;
616                                                 plcontext->ext_points_size = ns;
617                                         }
618                                         
619                                         // Parse extension point specification 
620                                         ext_point = plcontext->plugin->ext_points
621                                                 + plcontext->plugin->num_ext_points;
622                                         memset(ext_point, 0, sizeof(cp_ext_point_t));
623                                         ext_point->plugin = plcontext->plugin;
624                                         ext_point->name = NULL;
625                                         ext_point->local_id = NULL;
626                                         ext_point->identifier = NULL;
627                                         ext_point->schema_path = NULL;
628                                         for (i = 0; atts[i] != NULL; i += 2) {
629                                                 if (!strcmp(atts[i], "name")) {
630                                                         ext_point->name
631                                                                 = parser_strdup(plcontext, atts[i+1]);
632                                                 } else if (!strcmp(atts[i], "id")) {
633                                                         ext_point->local_id
634                                                                 = parser_strdup(plcontext, atts[i+1]);
635                                                         ext_point->identifier
636                                                                 = parser_strscat(plcontext,
637                                                                         plcontext->plugin->identifier, ".", atts[i+1], NULL);
638                                                 } else if (!strcmp(atts[i], "schema")) {
639                                                         ext_point->schema_path
640                                                                 = parser_strdup(plcontext, atts[i+1]);
641                                                 }
642                                         }
643                                         plcontext->plugin->num_ext_points++;
644                                         
645                                 }
646                         } else if (!(strcmp(name, "extension"))) {
647                                 plcontext->state = PARSER_EXTENSION;
648                                 plcontext->depth = 0;
649                                 if (check_req_attributes(
650                                         plcontext, name, atts, req_extension_atts)) {
651                                         cp_extension_t *extension;
652                                 
653                                         // Allocate space for extensions, if necessary 
654                                         if (plcontext->plugin->num_extensions == plcontext->extensions_size) {
655                                                 cp_extension_t *ne;
656                                                 size_t ns;
657                                                 
658                                                 if (plcontext->extensions_size == 0) {
659                                                         ns = 16;
660                                                 } else {
661                                                         ns = plcontext->extensions_size * 2;
662                                                 }
663                                                 if ((ne = realloc(plcontext->plugin->extensions,
664                                                                 ns * sizeof(cp_extension_t))) == NULL) {
665                                                         resource_error(plcontext);
666                                                         break;
667                                                 }
668                                                 plcontext->plugin->extensions = ne;
669                                                 plcontext->extensions_size = ns;
670                                         }
671                                         
672                                         // Parse extension attributes 
673                                         extension = plcontext->plugin->extensions
674                                                 + plcontext->plugin->num_extensions;
675                                         memset(extension, 0, sizeof(cp_extension_t));
676                                         extension->plugin = plcontext->plugin;
677                                         extension->name = NULL;
678                                         extension->local_id = NULL;
679                                         extension->identifier = NULL;
680                                         extension->ext_point_id = NULL;
681                                         extension->configuration = NULL;
682                                         for (i = 0; atts[i] != NULL; i += 2) {
683                                                 if (!strcmp(atts[i], "point")) {
684                                                         extension->ext_point_id
685                                                                 = parser_strdup(plcontext, atts[i+1]);
686                                                 } else if (!strcmp(atts[i], "id")) {
687                                                         extension->local_id
688                                                                 = parser_strdup(plcontext, atts[i+1]);
689                                                         extension->identifier
690                                                                 = parser_strscat(plcontext,
691                                                                         plcontext->plugin->identifier, ".", atts[i+1], NULL);
692                                                 } else if (!strcmp(atts[i], "name")) {
693                                                         extension->name
694                                                                 = parser_strdup(plcontext, atts[i+1]);
695                                                 }
696                                         }
697                                         plcontext->plugin->num_extensions++;
698                                         
699                                         // Initialize configuration parsing 
700                                         if ((extension->configuration = plcontext->configuration
701                                                 = parser_malloc(plcontext, sizeof(cp_cfg_element_t))) != NULL) {
702                                                 init_cfg_element(plcontext, plcontext->configuration, name, atts, NULL);
703                                         }
704                                         XML_SetCharacterDataHandler(plcontext->parser, character_data_handler);
705                                 }
706                         } else {
707                                 unexpected_element(plcontext, name);
708                         }
709                         break;
710
711                 case PARSER_REQUIRES:
712                         if (!strcmp(name, "c-pluff")) {
713                                 if (check_attributes(plcontext, name, atts,
714                                                 req_cpluff_atts, opt_cpluff_atts)) {
715                                         for (i = 0; atts[i] != NULL; i += 2) {
716                                                 if (!strcmp(atts[i], "version")) {
717                                                         plcontext->plugin->req_cpluff_version = parser_strdup(plcontext, atts[i+1]);
718                                                 }
719                                         }
720                                 }
721                         } else if (!strcmp(name, "import")) {
722                                 if (check_attributes(plcontext, name, atts,
723                                                 req_import_atts, opt_import_atts)) {
724                                         cp_plugin_import_t *import = NULL;
725                                 
726                                         // Allocate space for imports, if necessary 
727                                         if (plcontext->plugin->num_imports == plcontext->imports_size) {
728                                                 cp_plugin_import_t *ni;
729                                                 size_t ns;
730                                         
731                                                 if (plcontext->imports_size == 0) {
732                                                         ns = 16;
733                                                 } else {
734                                                         ns = plcontext->imports_size * 2;
735                                                 }
736                                                 if ((ni = realloc(plcontext->plugin->imports,
737                                                                 ns * sizeof(cp_plugin_import_t))) == NULL) {
738                                                         resource_error(plcontext);
739                                                         break;
740                                                 }
741                                                 plcontext->plugin->imports = ni;
742                                                 plcontext->imports_size = ns;
743                                         }
744                                 
745                                         // Parse import specification 
746                                         import = plcontext->plugin->imports
747                                                 + plcontext->plugin->num_imports;
748                                         memset(import, 0, sizeof(cp_plugin_import_t));
749                                         import->plugin_id = NULL;
750                                         import->version = NULL;
751                                         for (i = 0; atts[i] != NULL; i += 2) {
752                                                 if (!strcmp(atts[i], "plugin")) {
753                                                         import->plugin_id
754                                                                 = parser_strdup(plcontext, atts[i+1]);
755                                                 } else if (!strcmp(atts[i], "version")) {
756                                                         import->version = parser_strdup(plcontext, atts[i+1]);
757                                                 } else if (!strcmp(atts[i], "optional")) {
758                                                         if (!strcmp(atts[i+1], "true")
759                                                                 || !strcmp(atts[i+1], "1")) {
760                                                                 import->optional = 1;
761                                                         } else if (strcmp(atts[i+1], "false")
762                                                                 && strcmp(atts[i+1], "0")) {
763                                                                 descriptor_errorf(plcontext, 0, _("unknown boolean value: %s"), atts[i+1]);
764                                                         }
765                                                 }
766                                         }
767                                         plcontext->plugin->num_imports++;
768                                 }
769                         } else {
770                                 unexpected_element(plcontext, name);
771                         }
772                         break;
773
774                 case PARSER_EXTENSION:
775                         plcontext->depth++;
776                         if (plcontext->configuration != NULL && plcontext->skippedCEs == 0) {
777                                 cp_cfg_element_t *ce;
778                                 
779                                 // Allocate more space for children, if necessary 
780                                 if (plcontext->configuration->num_children == plcontext->configuration->index) {
781                                         cp_cfg_element_t *nce;
782                                         size_t ns;
783                                                 
784                                         if (plcontext->configuration->index == 0) {
785                                                 ns = 16;
786                                         } else {
787                                                 ns = plcontext->configuration->index * 2;
788                                         }
789                                         if ((nce = realloc(plcontext->configuration->children,
790                                                         ns * sizeof(cp_cfg_element_t))) == NULL) {
791                                                 plcontext->skippedCEs++;
792                                                 resource_error(plcontext);
793                                                 break;
794                                         }
795                                         plcontext->configuration->children = nce;
796                                         plcontext->configuration->index = ns;
797                                 }
798                                 
799                                 // Save possible value 
800                                 if (plcontext->value != NULL) {
801                                         plcontext->value[plcontext->value_length] = '\0';
802                                         plcontext->configuration->value = plcontext->value;
803                                 }
804                                 
805                                 ce = plcontext->configuration->children + plcontext->configuration->num_children;
806                                 init_cfg_element(plcontext, ce, name, atts, plcontext->configuration);
807                                 plcontext->configuration->num_children++;
808                                 plcontext->configuration = ce;
809                         }
810                         break;
811                         
812                 case PARSER_UNKNOWN:
813                         plcontext->depth++;
814                         break;
815                 default:
816                         unexpected_element(plcontext, name);
817                         break;
818         }
819 }
820
821 /**
822  * Processes the end of element events while parsing.
823  * 
824  * @param context the parsing context
825  * @param name the element name
826  */
827 static void CP_XMLCALL end_element_handler(
828         void *userData, const XML_Char *name) {
829         ploader_context_t *plcontext = userData;
830         
831         // Process element end 
832         switch (plcontext->state) {
833
834                 case PARSER_PLUGIN:
835                         if (!strcmp(name, "plugin")) {
836                                 
837                                 // Readjust memory allocated for extension points, if necessary 
838                                 if (plcontext->ext_points_size != plcontext->plugin->num_ext_points) {
839                                         cp_ext_point_t *nep;
840                                         
841                                         if ((nep = realloc(plcontext->plugin->ext_points,
842                                                         plcontext->plugin->num_ext_points *
843                                                                 sizeof(cp_ext_point_t))) != NULL
844                                                 || plcontext->plugin->num_ext_points == 0) {
845                                                 plcontext->plugin->ext_points = nep;
846                                                 plcontext->ext_points_size = plcontext->plugin->num_ext_points;
847                                         }
848                                 }
849                                 
850                                 // Readjust memory allocated for extensions, if necessary 
851                                 if (plcontext->extensions_size != plcontext->plugin->num_extensions) {
852                                         cp_extension_t *ne;
853                                         
854                                         if ((ne = realloc(plcontext->plugin->extensions,
855                                                         plcontext->plugin->num_extensions *
856                                                                 sizeof(cp_extension_t))) != NULL
857                                                 || plcontext->plugin->num_extensions == 0) {
858                                                 plcontext->plugin->extensions = ne;
859                                                 plcontext->extensions_size = plcontext->plugin->num_extensions;
860                                         }                                       
861                                 }
862                                 
863                                 plcontext->state = PARSER_END;
864                         }
865                         break;
866
867                 case PARSER_REQUIRES:
868                         if (!strcmp(name, "requires")) {
869                                 
870                                 // Readjust memory allocated for imports, if necessary 
871                                 if (plcontext->imports_size != plcontext->plugin->num_imports) {
872                                         cp_plugin_import_t *ni;
873                                         
874                                         if ((ni = realloc(plcontext->plugin->imports,
875                                                         plcontext->plugin->num_imports *
876                                                                 sizeof(cp_plugin_import_t))) != NULL
877                                                 || plcontext->plugin->num_imports == 0) {
878                                                 plcontext->plugin->imports = ni;
879                                                 plcontext->imports_size = plcontext->plugin->num_imports;
880                                         }
881                                 }
882                                 
883                                 plcontext->state = PARSER_PLUGIN;
884                         }
885                         break;
886
887                 case PARSER_UNKNOWN:
888                         if (plcontext->depth-- == 0) {
889                                 plcontext->state = plcontext->saved_state;
890                         }
891                         break;
892
893                 case PARSER_EXTENSION:
894                         if (plcontext->skippedCEs > 0) {
895                                 plcontext->skippedCEs--;
896                         } else if (plcontext->configuration != NULL) {
897                                 
898                                 // Readjust memory allocated for children, if necessary 
899                                 if (plcontext->configuration->index != plcontext->configuration->num_children) {
900                                         cp_cfg_element_t *nce;
901                                         
902                                         if ((nce = realloc(plcontext->configuration->children,
903                                                         plcontext->configuration->num_children *
904                                                                 sizeof(cp_cfg_element_t))) != NULL
905                                                 || plcontext->configuration->num_children == 0) {
906                                                 plcontext->configuration->children = nce;
907                                         }
908                                 }
909                                 
910                                 if (plcontext->configuration->parent != NULL) {
911                                         plcontext->configuration->index = plcontext->configuration->parent->num_children - 1;
912                                 } else {
913                                         plcontext->configuration->index = 0;
914                                 }
915                                 if (plcontext->value != NULL) {
916                                         char *v = plcontext->value;
917                                         int i;
918                                         
919                                         // Ignore trailing whitespace 
920                                         for (i = plcontext->value_length - 1; i >= 0; i--) {
921                                                 if (v[i] != ' ' && v[i] != '\n' && v[i] != '\r' && v[i] != '\t') {
922                                                         break;
923                                                 }
924                                         }
925                                         if (i  < 0) {
926                                                 free(plcontext->value);
927                                                 plcontext->value = NULL;
928                                                 plcontext->value_length = 0;
929                                                 plcontext->value_size = 0;
930                                         } else {
931                                                 plcontext->value_length = i + 1;
932                                         }
933                                 }
934                                 if (plcontext->value != NULL) {
935                                         
936                                         // Readjust memory allocated for value, if necessary 
937                                         if (plcontext->value_size > plcontext->value_length + 1) {
938                                                 char *nv;
939                                                 
940                                                 if ((nv = realloc(plcontext->value, (plcontext->value_length + 1) * sizeof(char))) != NULL) {
941                                                         plcontext->value = nv;
942                                                 }
943                                         }
944                                         
945                                         plcontext->value[plcontext->value_length] = '\0';
946                                         plcontext->configuration->value = plcontext->value;
947                                         plcontext->value = NULL;
948                                         plcontext->value_size = 0;
949                                         plcontext->value_length = 0;
950                                 }
951                                 plcontext->configuration = plcontext->configuration->parent;
952                                 
953                                 // Restore possible value 
954                                 if (plcontext->configuration != NULL
955                                         && plcontext->configuration->value != NULL) {
956                                         plcontext->value = plcontext->configuration->value;
957                                         plcontext->value_length = strlen(plcontext->value);
958                                         plcontext->value_size = CP_CFG_ELEMENT_VALUE_INITSIZE;
959                                         while (plcontext->value_size < plcontext->value_length + 1) {
960                                                 plcontext->value_size *= 2;
961                                         }
962                                 }
963                                 
964                         }                       
965                         if (plcontext->depth-- == 0) {
966                                 assert(!strcmp(name, "extension"));
967                                 plcontext->state = PARSER_PLUGIN;
968                                 XML_SetCharacterDataHandler(plcontext->parser, NULL);
969                         }
970                         break;
971                         
972                 default:
973                         descriptor_errorf(plcontext, 0, _("unexpected closing tag for %s"),
974                                 name);
975                         return;
976         }
977 }
978
979 static void dealloc_plugin_info(cp_context_t *ctx, cp_plugin_info_t *plugin) {
980         cpi_free_plugin(plugin);
981 }
982
983 CP_C_API cp_plugin_info_t * cp_load_plugin_descriptor(cp_context_t *context, const char *path, cp_status_t *error) {
984         char *file = NULL;
985         cp_status_t status = CP_OK;
986         FILE *fh = NULL;
987         XML_Parser parser = NULL;
988         ploader_context_t *plcontext = NULL;
989         cp_plugin_info_t *plugin = NULL;
990
991         CHECK_NOT_NULL(context);
992         CHECK_NOT_NULL(path);
993         cpi_lock_context(context);
994         cpi_check_invocation(context, CPI_CF_ANY, __func__);
995         do {
996                 int path_len;
997
998                 // Construct the file name for the plug-in descriptor 
999                 path_len = strlen(path);
1000                 if (path_len == 0) {
1001                         status = CP_ERR_IO;
1002                         break;
1003                 }
1004                 if (path[path_len - 1] == CP_FNAMESEP_CHAR) {
1005                         path_len--;
1006                 }
1007                 file = malloc((path_len + strlen(CP_PLUGIN_DESCRIPTOR) + 2) * sizeof(char));
1008                 if (file == NULL) {
1009                         status = CP_ERR_RESOURCE;
1010                         break;
1011                 }
1012                 strcpy(file, path);
1013                 file[path_len] = CP_FNAMESEP_CHAR;
1014                 strcpy(file + path_len + 1, CP_PLUGIN_DESCRIPTOR);
1015
1016                 // Open the file 
1017                 if ((fh = fopen(file, "rb")) == NULL) {
1018                         status = CP_ERR_IO;
1019                         break;
1020                 }
1021
1022                 // Initialize the XML parsing 
1023                 parser = XML_ParserCreate(NULL);
1024                 if (parser == NULL) {
1025                         status = CP_ERR_RESOURCE;
1026                         break;
1027                 }
1028                 XML_SetElementHandler(parser,
1029                         start_element_handler,
1030                         end_element_handler);
1031                 
1032                 // Initialize the parsing context 
1033                 if ((plcontext = malloc(sizeof(ploader_context_t))) == NULL) {
1034                         status = CP_ERR_RESOURCE;
1035                         break;
1036                 }
1037                 memset(plcontext, 0, sizeof(ploader_context_t));
1038                 if ((plcontext->plugin = malloc(sizeof(cp_plugin_info_t))) == NULL) {
1039                         status = CP_ERR_RESOURCE;
1040                         break;
1041                 }
1042                 plcontext->context = context;
1043                 plcontext->configuration = NULL;
1044                 plcontext->value = NULL;
1045                 plcontext->parser = parser;
1046                 plcontext->file = file;
1047                 plcontext->state = PARSER_BEGIN;
1048                 memset(plcontext->plugin, 0, sizeof(cp_plugin_info_t));
1049                 plcontext->plugin->name = NULL;
1050                 plcontext->plugin->identifier = NULL;
1051                 plcontext->plugin->version = NULL;
1052                 plcontext->plugin->provider_name = NULL;
1053                 plcontext->plugin->abi_bw_compatibility = NULL;
1054                 plcontext->plugin->api_bw_compatibility = NULL;
1055                 plcontext->plugin->plugin_path = NULL;
1056                 plcontext->plugin->req_cpluff_version = NULL;
1057                 plcontext->plugin->imports = NULL;
1058                 plcontext->plugin->runtime_lib_name = NULL;
1059                 plcontext->plugin->runtime_funcs_symbol = NULL;
1060                 plcontext->plugin->ext_points = NULL;
1061                 plcontext->plugin->extensions = NULL;
1062                 plcontext->plugin->url = NULL;
1063                 plcontext->plugin->resourcetype = NULL;
1064                 XML_SetUserData(parser, plcontext);
1065
1066                 // Parse the plug-in descriptor 
1067                 while (1) {
1068                         int bytes_read;
1069                         void *xml_buffer;
1070                         int i;
1071                         
1072                         // Get buffer from Expat 
1073                         if ((xml_buffer = XML_GetBuffer(parser, CP_XML_PARSER_BUFFER_SIZE))
1074                                 == NULL) {
1075                                 status = CP_ERR_RESOURCE;
1076                                 break;
1077                         }
1078                         
1079                         // Read data into buffer 
1080                         bytes_read = fread(xml_buffer, 1, CP_XML_PARSER_BUFFER_SIZE, fh);
1081                         if (ferror(fh)) {
1082                                 status = CP_ERR_IO;
1083                                 break;
1084                         }
1085
1086                         // Parse the data 
1087                         if (!(i = XML_ParseBuffer(parser, bytes_read, bytes_read == 0))
1088                                 && context != NULL) {
1089                                 cpi_lock_context(context);
1090                                 cpi_errorf(context,
1091                                         N_("XML parsing error in %s, line %d, column %d (%s)."),
1092                                         file,
1093                                         XML_GetErrorLineNumber(parser),
1094                                         XML_GetErrorColumnNumber(parser) + 1,
1095                                         XML_ErrorString(XML_GetErrorCode(parser)));
1096                                 cpi_unlock_context(context);
1097                         }
1098                         if (!i || plcontext->state == PARSER_ERROR) {
1099                                 status = CP_ERR_MALFORMED;
1100                                 break;
1101                         }
1102                         
1103                         if (bytes_read == 0) {
1104                                 break;
1105                         }
1106                 }
1107                 if (status == CP_OK) {
1108                         if (plcontext->state != PARSER_END || plcontext->error_count > 0) {
1109                                 status = CP_ERR_MALFORMED;
1110                         }
1111                         if (plcontext->resource_error_count > 0) {
1112                                 status = CP_ERR_RESOURCE;
1113                         }
1114                 }
1115                 if (status != CP_OK) {
1116                         break;
1117                 }
1118
1119                 // Initialize the plug-in path 
1120                 *(file + path_len) = '\0';
1121                 plcontext->plugin->plugin_path = file;
1122                 file = NULL;
1123                 
1124                 // Increase plug-in usage count
1125                 if ((status = cpi_register_info(context, plcontext->plugin, (void (*)(cp_context_t *, void *)) dealloc_plugin_info)) != CP_OK) {
1126                         break;
1127                 }
1128                 
1129         } while (0);
1130
1131         // Report possible errors
1132         if (status != CP_OK) {
1133                 switch (status) {
1134                         case CP_ERR_MALFORMED:
1135                                 cpi_errorf(context,
1136                                         N_("Plug-in descriptor in %s is invalid."), path);
1137                                 break;
1138                         case CP_ERR_IO:
1139                                 cpi_errorf(context,
1140                                         N_("An I/O error occurred while loading a plug-in descriptor from %s."), path);
1141                                 break;
1142                         case CP_ERR_RESOURCE:
1143                                 cpi_errorf(context,
1144                                         N_("Insufficient system resources to load a plug-in descriptor from %s."), path);
1145                                 break;
1146                         default:
1147                                 cpi_errorf(context,
1148                                         N_("Failed to load a plug-in descriptor from %s."), path);
1149                                 break;
1150                 }
1151         }
1152         cpi_unlock_context(context);
1153
1154         // Release persistently allocated data on failure 
1155         if (status != CP_OK) {
1156                 if (file != NULL) {
1157                         free(file);
1158                         file = NULL;
1159                 }
1160                 if (plcontext != NULL && plcontext->plugin != NULL) {
1161                         cpi_free_plugin(plcontext->plugin);
1162                         plcontext->plugin = NULL;
1163                 }
1164         }
1165         
1166         // Otherwise copy the plug-in pointer
1167         else {
1168                 plugin = plcontext->plugin;
1169         }
1170
1171         // Release data allocated for parsing 
1172         if (parser != NULL) {
1173                 XML_ParserFree(parser);
1174         }
1175         if (fh != NULL) {
1176                 fclose(fh);
1177         }
1178         if (plcontext != NULL) {
1179                 if (plcontext->value != NULL) {
1180                         free(plcontext->value);
1181                 }
1182                 free(plcontext);
1183                 plcontext = NULL;
1184         }
1185
1186         // Return error code
1187         if (error != NULL) {
1188                 *error = status;
1189         }
1190
1191         return plugin;
1192 }