1 /*-------------------------------------------------------------------------
2 * C-Pluff, a plug-in framework for C
3 * Copyright 2007 Johannes Lehtinen
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:
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
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 *-----------------------------------------------------------------------*/
25 * Plug-in descriptor loader
41 // Use XMLCALL if available
43 #define CP_XMLCALL XMLCALL
49 /* ------------------------------------------------------------------------
51 * ----------------------------------------------------------------------*/
53 /// XML parser buffer size (in bytes)
54 #define CP_XML_PARSER_BUFFER_SIZE 4096
56 /// Initial configuration element value size
57 #define CP_CFG_ELEMENT_VALUE_INITSIZE 64
59 /// Plugin descriptor name
60 #define CP_PLUGIN_DESCRIPTOR "plugin.xml"
63 /* ------------------------------------------------------------------------
65 * ----------------------------------------------------------------------*/
67 typedef struct ploader_context_t ploader_context_t;
70 typedef enum parser_state_t {
80 /// Plug-in loader context
81 struct ploader_context_t {
83 /// The plug-in context, or NULL if none
84 cp_context_t *context;
86 /// The XML parser being used
89 /// The file being parsed
92 /// The plug-in being constructed
93 cp_plugin_info_t *plugin;
95 /// The configuration element being constructed
96 cp_cfg_element_t *configuration;
98 /// The current parser state
101 /// The saved parser state (used in PARSER_UNKNOWN)
102 parser_state_t saved_state;
105 * The current parser depth (used in PARSER_UNKNOWN and PARSER_EXTENSION)
109 /// The number of skipped configuration elements
110 unsigned int skippedCEs;
112 /// Size of allocated imports table
115 /// Size of allocated extension points table
116 size_t ext_points_size;
118 /// Size of allocated extensions table
119 size_t extensions_size;
121 /// Buffer for a value being read
124 /// Size of allocated value field
127 /// Current length of value string
130 /// The number of parsing errors that have occurred
131 unsigned int error_count;
133 /// The number of resource errors that have occurred
134 unsigned int resource_error_count;
138 /* ------------------------------------------------------------------------
139 * Function definitions
140 * ----------------------------------------------------------------------*/
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.
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
151 static void descriptor_errorf(ploader_context_t *plcontext, int warn,
152 const char *error_msg, ...) {
156 va_start(ap, error_msg);
157 vsnprintf(message, sizeof(message), error_msg, ap);
161 cpi_warnf(plcontext->context,
162 N_("Suspicious plug-in descriptor content in %s, line %d, column %d (%s)."),
164 XML_GetCurrentLineNumber(plcontext->parser),
165 XML_GetCurrentColumnNumber(plcontext->parser) + 1,
168 cpi_errorf(plcontext->context,
169 N_("Invalid plug-in descriptor content in %s, line %d, column %d (%s)."),
171 XML_GetCurrentLineNumber(plcontext->parser),
172 XML_GetCurrentColumnNumber(plcontext->parser) + 1,
176 plcontext->error_count++;
181 * Reports insufficient system resources while parsing and increments the
182 * resource error count.
184 * @param context the parsing context
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."),
191 XML_GetCurrentLineNumber(plcontext->parser),
192 XML_GetCurrentColumnNumber(plcontext->parser) + 1);
194 plcontext->resource_error_count++;
198 * Returns whether the specified NULL-terminated list of strings includes
199 * the specified string.
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
205 * @return pointer to the location of the string or NULL if not found
207 static const XML_Char * const *contains_str(const XML_Char * const *list,
208 const XML_Char *str, int step) {
210 while (*list != NULL) {
211 if (!strcmp(*list, str)) {
221 * Checks that an element has non-empty values for required attributes.
222 * Increments the error count for each missing attribute.
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
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;
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;
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"),
248 descriptor_errorf(plcontext, 0,
249 _("required attribute %s missing for element %s"),
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.
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
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) {
275 // Check required attributes
276 error = !check_req_attributes(plcontext, elem, atts, req_atts);
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"),
292 * Allocates memory using malloc. Reports a resource error if there is not
293 * enough available memory.
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
299 static void *parser_malloc(ploader_context_t *plcontext, size_t size) {
302 if ((ptr = malloc(size)) == NULL) {
303 resource_error(plcontext);
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.
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
316 static char *parser_strdup(ploader_context_t *plcontext, const char *src) {
319 if ((dup = strdup(src)) == NULL) {
320 resource_error(plcontext);
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.
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
334 static char *parser_strscat(ploader_context_t *plcontext, ...) {
340 // Calculate the length of the concatenated string
341 va_start(ap, plcontext);
343 while ((str = va_arg(ap, const char *)) != NULL) {
348 // Allocate space for the concatenated string
349 if ((dst = parser_malloc(plcontext, sizeof(char) * (len + 1))) == NULL) {
355 va_start(ap, plcontext);
356 while ((str = va_arg(ap, const char *)) != NULL) {
357 strcpy(dst + len, str);
366 * Puts the parser to a state in which it skips an unknown element.
367 * Warns error handlers about the unknown element.
369 * @param context the parsing context
370 * @param elem the element name
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);
380 * Creates a copy of the specified attributes. Reports failed memory
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,
387 * @return the duplicated attribute array, or NULL if empty or failed
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;
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;
400 assert((num & 1) == 0);
402 // Allocate necessary memory and copy attribute data
404 if ((atts = parser_malloc(plcontext, num * sizeof(char *))) != NULL) {
405 if ((attr_data = parser_malloc(plcontext, attr_size * sizeof(char))) != NULL) {
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;
417 // If successful then return duplicates, otherwise free any allocations
418 if (num == 0 || (atts != NULL && attr_data != NULL)) {
419 if (num_atts != NULL) {
431 * Initializes a configuration element. Reports an error if memory allocation fails.
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
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) {
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));
447 plcontext->value = NULL;
448 plcontext->value_size = 0;
449 plcontext->value_length = 0;
455 * Processes the character data while parsing.
457 * @param userData the parsing context
458 * @param str the string data
459 * @param len the string length
461 static void CP_XMLCALL character_data_handler(
462 void *userData, const XML_Char *str, int len) {
463 ploader_context_t *plcontext = userData;
465 // Ignore leading whitespace
466 if (plcontext->value == NULL) {
469 for (i = 0; i < len; i++) {
470 if (str[i] != ' ' && str[i] != '\n' && str[i] != '\r' && str[i] != '\t') {
481 // Allocate more memory for the character data if needed
482 if (plcontext->value_length + len >= plcontext->value_size) {
486 ns = plcontext->value_size;
487 while (plcontext->value_length + len >= ns) {
489 ns = CP_CFG_ELEMENT_VALUE_INITSIZE;
494 if ((nv = realloc(plcontext->value, ns * sizeof(char))) != NULL) {
495 plcontext->value = nv;
496 plcontext->value_size = ns;
498 resource_error(plcontext);
503 // Copy character data
504 strncpy(plcontext->value + plcontext->value_length, str, len * sizeof(char));
505 plcontext->value_length += len;
509 * Processes the start of element events while parsing.
511 * @param userData the parsing context
512 * @param name the element name
513 * @param atts the element attributes
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;
534 // Process element start
535 switch (plcontext->state) {
538 if (!strcmp(name, "plugin")) {
539 plcontext->state = PARSER_PLUGIN;
540 if (!check_attributes(plcontext, name, atts,
541 req_plugin_atts, opt_plugin_atts)) {
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]);
564 unexpected_element(plcontext, name);
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]);
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]);
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;
600 // Allocate space for extension points, if necessary
601 if (plcontext->plugin->num_ext_points == plcontext->ext_points_size) {
605 if (plcontext->ext_points_size == 0) {
608 ns = plcontext->ext_points_size * 2;
610 if ((nep = realloc(plcontext->plugin->ext_points,
611 ns * sizeof(cp_ext_point_t))) == NULL) {
612 resource_error(plcontext);
615 plcontext->plugin->ext_points = nep;
616 plcontext->ext_points_size = ns;
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")) {
631 = parser_strdup(plcontext, atts[i+1]);
632 } else if (!strcmp(atts[i], "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]);
643 plcontext->plugin->num_ext_points++;
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;
653 // Allocate space for extensions, if necessary
654 if (plcontext->plugin->num_extensions == plcontext->extensions_size) {
658 if (plcontext->extensions_size == 0) {
661 ns = plcontext->extensions_size * 2;
663 if ((ne = realloc(plcontext->plugin->extensions,
664 ns * sizeof(cp_extension_t))) == NULL) {
665 resource_error(plcontext);
668 plcontext->plugin->extensions = ne;
669 plcontext->extensions_size = ns;
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")) {
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")) {
694 = parser_strdup(plcontext, atts[i+1]);
697 plcontext->plugin->num_extensions++;
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);
704 XML_SetCharacterDataHandler(plcontext->parser, character_data_handler);
707 unexpected_element(plcontext, name);
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]);
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;
726 // Allocate space for imports, if necessary
727 if (plcontext->plugin->num_imports == plcontext->imports_size) {
728 cp_plugin_import_t *ni;
731 if (plcontext->imports_size == 0) {
734 ns = plcontext->imports_size * 2;
736 if ((ni = realloc(plcontext->plugin->imports,
737 ns * sizeof(cp_plugin_import_t))) == NULL) {
738 resource_error(plcontext);
741 plcontext->plugin->imports = ni;
742 plcontext->imports_size = ns;
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")) {
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]);
767 plcontext->plugin->num_imports++;
770 unexpected_element(plcontext, name);
774 case PARSER_EXTENSION:
776 if (plcontext->configuration != NULL && plcontext->skippedCEs == 0) {
777 cp_cfg_element_t *ce;
779 // Allocate more space for children, if necessary
780 if (plcontext->configuration->num_children == plcontext->configuration->index) {
781 cp_cfg_element_t *nce;
784 if (plcontext->configuration->index == 0) {
787 ns = plcontext->configuration->index * 2;
789 if ((nce = realloc(plcontext->configuration->children,
790 ns * sizeof(cp_cfg_element_t))) == NULL) {
791 plcontext->skippedCEs++;
792 resource_error(plcontext);
795 plcontext->configuration->children = nce;
796 plcontext->configuration->index = ns;
799 // Save possible value
800 if (plcontext->value != NULL) {
801 plcontext->value[plcontext->value_length] = '\0';
802 plcontext->configuration->value = plcontext->value;
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;
816 unexpected_element(plcontext, name);
822 * Processes the end of element events while parsing.
824 * @param context the parsing context
825 * @param name the element name
827 static void CP_XMLCALL end_element_handler(
828 void *userData, const XML_Char *name) {
829 ploader_context_t *plcontext = userData;
831 // Process element end
832 switch (plcontext->state) {
835 if (!strcmp(name, "plugin")) {
837 // Readjust memory allocated for extension points, if necessary
838 if (plcontext->ext_points_size != plcontext->plugin->num_ext_points) {
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;
850 // Readjust memory allocated for extensions, if necessary
851 if (plcontext->extensions_size != plcontext->plugin->num_extensions) {
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;
863 plcontext->state = PARSER_END;
867 case PARSER_REQUIRES:
868 if (!strcmp(name, "requires")) {
870 // Readjust memory allocated for imports, if necessary
871 if (plcontext->imports_size != plcontext->plugin->num_imports) {
872 cp_plugin_import_t *ni;
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;
883 plcontext->state = PARSER_PLUGIN;
888 if (plcontext->depth-- == 0) {
889 plcontext->state = plcontext->saved_state;
893 case PARSER_EXTENSION:
894 if (plcontext->skippedCEs > 0) {
895 plcontext->skippedCEs--;
896 } else if (plcontext->configuration != NULL) {
898 // Readjust memory allocated for children, if necessary
899 if (plcontext->configuration->index != plcontext->configuration->num_children) {
900 cp_cfg_element_t *nce;
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;
910 if (plcontext->configuration->parent != NULL) {
911 plcontext->configuration->index = plcontext->configuration->parent->num_children - 1;
913 plcontext->configuration->index = 0;
915 if (plcontext->value != NULL) {
916 char *v = plcontext->value;
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') {
926 free(plcontext->value);
927 plcontext->value = NULL;
928 plcontext->value_length = 0;
929 plcontext->value_size = 0;
931 plcontext->value_length = i + 1;
934 if (plcontext->value != NULL) {
936 // Readjust memory allocated for value, if necessary
937 if (plcontext->value_size > plcontext->value_length + 1) {
940 if ((nv = realloc(plcontext->value, (plcontext->value_length + 1) * sizeof(char))) != NULL) {
941 plcontext->value = nv;
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;
951 plcontext->configuration = plcontext->configuration->parent;
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;
965 if (plcontext->depth-- == 0) {
966 assert(!strcmp(name, "extension"));
967 plcontext->state = PARSER_PLUGIN;
968 XML_SetCharacterDataHandler(plcontext->parser, NULL);
973 descriptor_errorf(plcontext, 0, _("unexpected closing tag for %s"),
979 static void dealloc_plugin_info(cp_context_t *ctx, cp_plugin_info_t *plugin) {
980 cpi_free_plugin(plugin);
983 CP_C_API cp_plugin_info_t * cp_load_plugin_descriptor(cp_context_t *context, const char *path, cp_status_t *error) {
985 cp_status_t status = CP_OK;
987 XML_Parser parser = NULL;
988 ploader_context_t *plcontext = NULL;
989 cp_plugin_info_t *plugin = NULL;
991 CHECK_NOT_NULL(context);
992 CHECK_NOT_NULL(path);
993 cpi_lock_context(context);
994 cpi_check_invocation(context, CPI_CF_ANY, __func__);
998 // Construct the file name for the plug-in descriptor
999 path_len = strlen(path);
1000 if (path_len == 0) {
1004 if (path[path_len - 1] == CP_FNAMESEP_CHAR) {
1007 file = malloc((path_len + strlen(CP_PLUGIN_DESCRIPTOR) + 2) * sizeof(char));
1009 status = CP_ERR_RESOURCE;
1013 file[path_len] = CP_FNAMESEP_CHAR;
1014 strcpy(file + path_len + 1, CP_PLUGIN_DESCRIPTOR);
1017 if ((fh = fopen(file, "rb")) == NULL) {
1022 // Initialize the XML parsing
1023 parser = XML_ParserCreate(NULL);
1024 if (parser == NULL) {
1025 status = CP_ERR_RESOURCE;
1028 XML_SetElementHandler(parser,
1029 start_element_handler,
1030 end_element_handler);
1032 // Initialize the parsing context
1033 if ((plcontext = malloc(sizeof(ploader_context_t))) == NULL) {
1034 status = CP_ERR_RESOURCE;
1037 memset(plcontext, 0, sizeof(ploader_context_t));
1038 if ((plcontext->plugin = malloc(sizeof(cp_plugin_info_t))) == NULL) {
1039 status = CP_ERR_RESOURCE;
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);
1066 // Parse the plug-in descriptor
1072 // Get buffer from Expat
1073 if ((xml_buffer = XML_GetBuffer(parser, CP_XML_PARSER_BUFFER_SIZE))
1075 status = CP_ERR_RESOURCE;
1079 // Read data into buffer
1080 bytes_read = fread(xml_buffer, 1, CP_XML_PARSER_BUFFER_SIZE, fh);
1087 if (!(i = XML_ParseBuffer(parser, bytes_read, bytes_read == 0))
1088 && context != NULL) {
1089 cpi_lock_context(context);
1091 N_("XML parsing error in %s, line %d, column %d (%s)."),
1093 XML_GetErrorLineNumber(parser),
1094 XML_GetErrorColumnNumber(parser) + 1,
1095 XML_ErrorString(XML_GetErrorCode(parser)));
1096 cpi_unlock_context(context);
1098 if (!i || plcontext->state == PARSER_ERROR) {
1099 status = CP_ERR_MALFORMED;
1103 if (bytes_read == 0) {
1107 if (status == CP_OK) {
1108 if (plcontext->state != PARSER_END || plcontext->error_count > 0) {
1109 status = CP_ERR_MALFORMED;
1111 if (plcontext->resource_error_count > 0) {
1112 status = CP_ERR_RESOURCE;
1115 if (status != CP_OK) {
1119 // Initialize the plug-in path
1120 *(file + path_len) = '\0';
1121 plcontext->plugin->plugin_path = file;
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) {
1131 // Report possible errors
1132 if (status != CP_OK) {
1134 case CP_ERR_MALFORMED:
1136 N_("Plug-in descriptor in %s is invalid."), path);
1140 N_("An I/O error occurred while loading a plug-in descriptor from %s."), path);
1142 case CP_ERR_RESOURCE:
1144 N_("Insufficient system resources to load a plug-in descriptor from %s."), path);
1148 N_("Failed to load a plug-in descriptor from %s."), path);
1152 cpi_unlock_context(context);
1154 // Release persistently allocated data on failure
1155 if (status != CP_OK) {
1160 if (plcontext != NULL && plcontext->plugin != NULL) {
1161 cpi_free_plugin(plcontext->plugin);
1162 plcontext->plugin = NULL;
1166 // Otherwise copy the plug-in pointer
1168 plugin = plcontext->plugin;
1171 // Release data allocated for parsing
1172 if (parser != NULL) {
1173 XML_ParserFree(parser);
1178 if (plcontext != NULL) {
1179 if (plcontext->value != NULL) {
1180 free(plcontext->value);
1186 // Return error code
1187 if (error != NULL) {