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
29 #ifndef _POSIX_C_SOURCE
30 // Defining _POSIX_C_SOURCE macro with 200809L (or greater) as value
31 // causes header files to expose definitions
32 // corresponding to the POSIX.1-2008 base
33 // specification (excluding the XSI extension).
34 // For POSIX.1-2008 base specification,
35 // Refer http://pubs.opengroup.org/stage7tc1/
37 // For this specific file, see use of strndup,
38 // Refer http://man7.org/linux/man-pages/man3/strdup.3.html
39 #define _POSIX_C_SOURCE 200809L
56 // Use XMLCALL if available
58 #define CP_XMLCALL XMLCALL
64 /* ------------------------------------------------------------------------
66 * ----------------------------------------------------------------------*/
68 /// XML parser buffer size (in bytes)
69 #define CP_XML_PARSER_BUFFER_SIZE 4096
71 /// Initial configuration element value size
72 #define CP_CFG_ELEMENT_VALUE_INITSIZE 64
74 /// Plugin descriptor name
75 #define CP_PLUGIN_DESCRIPTOR "plugin.xml"
78 /* ------------------------------------------------------------------------
80 * ----------------------------------------------------------------------*/
82 typedef struct ploader_context_t ploader_context_t;
85 typedef enum parser_state_t {
95 /// Plug-in loader context
96 struct ploader_context_t {
98 /// The plug-in context, or NULL if none
99 cp_context_t *context;
101 /// The XML parser being used
104 /// The file being parsed
107 /// The plug-in being constructed
108 cp_plugin_info_t *plugin;
110 /// The configuration element being constructed
111 cp_cfg_element_t *configuration;
113 /// The current parser state
114 parser_state_t state;
116 /// The saved parser state (used in PARSER_UNKNOWN)
117 parser_state_t saved_state;
120 * The current parser depth (used in PARSER_UNKNOWN and PARSER_EXTENSION)
124 /// The number of skipped configuration elements
125 unsigned int skippedCEs;
127 /// Size of allocated imports table
130 /// Size of allocated extension points table
131 size_t ext_points_size;
133 /// Size of allocated extensions table
134 size_t extensions_size;
136 /// Buffer for a value being read
139 /// Size of allocated value field
142 /// Current length of value string
145 /// The number of parsing errors that have occurred
146 unsigned int error_count;
148 /// The number of resource errors that have occurred
149 unsigned int resource_error_count;
153 /* ------------------------------------------------------------------------
154 * Function definitions
155 * ----------------------------------------------------------------------*/
158 * Reports a descriptor error. Does not set the parser to error state but
159 * increments the error count, unless this is merely a warning.
161 * @param context the parsing context
162 * @param warn whether this is only a warning
163 * @param error_msg the error message
164 * @param ... parameters for the error message
166 static void descriptor_errorf(ploader_context_t *plcontext, int warn,
167 const char *error_msg, ...) {
171 va_start(ap, error_msg);
172 vsnprintf(message, sizeof(message), error_msg, ap);
176 cpi_warnf(plcontext->context,
177 N_("Suspicious plug-in descriptor content in %s, line %d, column %d (%s)."),
179 XML_GetCurrentLineNumber(plcontext->parser),
180 XML_GetCurrentColumnNumber(plcontext->parser) + 1,
183 cpi_errorf(plcontext->context,
184 N_("Invalid plug-in descriptor content in %s, line %d, column %d (%s)."),
186 XML_GetCurrentLineNumber(plcontext->parser),
187 XML_GetCurrentColumnNumber(plcontext->parser) + 1,
191 plcontext->error_count++;
196 * Reports insufficient system resources while parsing and increments the
197 * resource error count.
199 * @param context the parsing context
201 static void resource_error(ploader_context_t *plcontext) {
202 if (plcontext->resource_error_count == 0) {
203 cpi_errorf(plcontext->context,
204 N_("Insufficient system resources to parse plug-in descriptor content in %s, line %d, column %d."),
206 XML_GetCurrentLineNumber(plcontext->parser),
207 XML_GetCurrentColumnNumber(plcontext->parser) + 1);
209 plcontext->resource_error_count++;
213 * Returns whether the specified NULL-terminated list of strings includes
214 * the specified string.
216 * @param list the NULL-terminated list of strings, or NULL if none
217 * @param str the string
218 * @param step the stepping (1 to check every string or 2 to check every
220 * @return pointer to the location of the string or NULL if not found
222 static const XML_Char * const *contains_str(const XML_Char * const *list,
223 const XML_Char *str, int step) {
225 while (*list != NULL) {
226 if (!strcmp(*list, str)) {
236 * Checks that an element has non-empty values for required attributes.
237 * Increments the error count for each missing attribute.
239 * @param context the parsing context
240 * @param elem the element being checked
241 * @param atts the attribute list for the element
242 * @param req_atts the required attributes (NULL terminated list, or NULL)
243 * @return whether the required attributes are present
245 static int check_req_attributes(ploader_context_t *plcontext,
246 const XML_Char *elem, const XML_Char * const *atts,
247 const XML_Char * const *req_atts) {
248 const XML_Char * const *a;
251 // Check that required attributes have non-empty values
252 for (a = req_atts; a != NULL && *a != NULL; a++) {
253 const XML_Char * const *av;
255 if ((av = contains_str(atts, *a, 2)) != NULL) {
256 if ((*(av + 1))[0] == '\0') {
257 descriptor_errorf(plcontext, 0,
258 _("required attribute %s for element %s has an empty value"),
263 descriptor_errorf(plcontext, 0,
264 _("required attribute %s missing for element %s"),
274 * Checks that an element has non-empty values for required attributes and
275 * warns if there are unknown attributes. Increments the error count for
276 * each missing required attribute.
278 * @param context the parsing context
279 * @param elem the element being checked
280 * @param atts the attribute list for the element
281 * @param req_atts the required attributes (NULL terminated list, or NULL)
282 * @param opt_atts the optional attributes (NULL terminated list, or NULL)
283 * @return whether the required attributes are present
285 static int check_attributes(ploader_context_t *plcontext,
286 const XML_Char *elem, const XML_Char * const *atts,
287 const XML_Char * const *req_atts, const XML_Char * const *opt_atts) {
290 // Check required attributes
291 error = !check_req_attributes(plcontext, elem, atts, req_atts);
293 // Warn if there are unknown attributes
294 for (; *atts != NULL; atts += 2) {
295 if (contains_str(req_atts, *atts, 1) == NULL
296 && contains_str(opt_atts, *atts, 1) == NULL) {
297 descriptor_errorf(plcontext, 1,
298 _("ignoring unknown attribute %s for element %s"),
307 * Allocates memory using malloc. Reports a resource error if there is not
308 * enough available memory.
310 * @param context the parsing context
311 * @param size the number of bytes to allocate
312 * @return pointer to the allocated memory, or NULL if memory allocation failed
314 static void *parser_malloc(ploader_context_t *plcontext, size_t size) {
317 if ((ptr = malloc(size)) == NULL) {
318 resource_error(plcontext);
324 * Makes a copy of the specified string. The memory is allocated using malloc.
325 * Reports a resource error if there is not enough available memory.
327 * @param context the parsing context
328 * @param src the source string to be copied
329 * @return copy of the string, or NULL if memory allocation failed
331 static char *parser_strdup(ploader_context_t *plcontext, const char *src) {
334 if ((dup = strdup(src)) == NULL) {
335 resource_error(plcontext);
341 * Concatenates the specified strings into a new string. The memory for the concatenated
342 * string is allocated using malloc. Reports a resource error if there is not
343 * enough available memory.
345 * @param context the parsing context
346 * @param ... the strings to be concatenated, terminated by NULL
347 * @return the concatenated string, or NULL if memory allocation failed
349 static char *parser_strscat(ploader_context_t *plcontext, ...) {
355 // Calculate the length of the concatenated string
356 va_start(ap, plcontext);
358 while ((str = va_arg(ap, const char *)) != NULL) {
363 // Allocate space for the concatenated string
364 if ((dst = parser_malloc(plcontext, sizeof(char) * (len + 1))) == NULL) {
370 va_start(ap, plcontext);
371 while ((str = va_arg(ap, const char *)) != NULL) {
372 strcpy(dst + len, str);
381 * Puts the parser to a state in which it skips an unknown element.
382 * Warns error handlers about the unknown element.
384 * @param context the parsing context
385 * @param elem the element name
387 static void unexpected_element(ploader_context_t *plcontext, const XML_Char *elem) {
388 plcontext->saved_state = plcontext->state;
389 plcontext->state = PARSER_UNKNOWN;
390 plcontext->depth = 0;
391 descriptor_errorf(plcontext, 1, _("ignoring unexpected element %s and its contents"), elem);
395 * Creates a copy of the specified attributes. Reports failed memory
398 * @param context the parser context
399 * @param src the source attributes to be copied
400 * @param num pointer to the location where number of attributes is stored,
402 * @return the duplicated attribute array, or NULL if empty or failed
404 static char **parser_attsdup(ploader_context_t *plcontext, const XML_Char * const *src,
405 unsigned int *num_atts) {
406 char **atts = NULL, *attr_data = NULL;
411 // Calculate the number of attributes and the amount of space required
412 for (num = 0, attr_size = 0; src[num] != NULL; num++) {
413 attr_size += strlen(src[num]) + 1;
415 assert((num & 1) == 0);
417 // Allocate necessary memory and copy attribute data
419 if ((atts = parser_malloc(plcontext, num * sizeof(char *))) != NULL) {
420 if ((attr_data = parser_malloc(plcontext, attr_size * sizeof(char))) != NULL) {
423 for (i = 0, offset = 0; i < num; i++) {
424 strcpy(attr_data + offset, src[i]);
425 atts[i] = attr_data + offset;
426 offset += strlen(src[i]) + 1;
432 // If successful then return duplicates, otherwise free any allocations
433 if (num == 0 || (atts != NULL && attr_data != NULL)) {
434 if (num_atts != NULL) {
446 * Initializes a configuration element. Reports an error if memory allocation fails.
448 * @param context the parser context
449 * @param ce the configuration element to be initialized
450 * @param name the element name
451 * @param atts the element attributes
452 * @param parent the parent element
454 static void init_cfg_element(ploader_context_t *plcontext, cp_cfg_element_t *ce,
455 const XML_Char *name, const XML_Char * const *atts, cp_cfg_element_t *parent) {
457 // Initialize the configuration element
458 memset(ce, 0, sizeof(cp_cfg_element_t));
459 ce->name = parser_strdup(plcontext, name);
460 ce->atts = parser_attsdup(plcontext, atts, &(ce->num_atts));
462 plcontext->value = NULL;
463 plcontext->value_size = 0;
464 plcontext->value_length = 0;
470 * Processes the character data while parsing.
472 * @param userData the parsing context
473 * @param str the string data
474 * @param len the string length
476 static void CP_XMLCALL character_data_handler(
477 void *userData, const XML_Char *str, int len) {
478 ploader_context_t *plcontext = userData;
480 // Ignore leading whitespace
481 if (plcontext->value == NULL) {
484 for (i = 0; i < len; i++) {
485 if (str[i] != ' ' && str[i] != '\n' && str[i] != '\r' && str[i] != '\t') {
496 // Allocate more memory for the character data if needed
497 if (plcontext->value_length + len >= plcontext->value_size) {
501 ns = plcontext->value_size;
502 while (plcontext->value_length + len >= ns) {
504 ns = CP_CFG_ELEMENT_VALUE_INITSIZE;
509 if ((nv = realloc(plcontext->value, ns * sizeof(char))) != NULL) {
510 plcontext->value = nv;
511 plcontext->value_size = ns;
513 resource_error(plcontext);
518 // Copy character data
519 strncpy(plcontext->value + plcontext->value_length, str, len * sizeof(char));
520 plcontext->value_length += len;
524 * Processes the start of element events while parsing.
526 * @param userData the parsing context
527 * @param name the element name
528 * @param atts the element attributes
530 static void CP_XMLCALL start_element_handler(
531 void *userData, const XML_Char *name, const XML_Char **atts) {
532 static const XML_Char * const req_plugin_atts[] = { "id", NULL };
533 static const XML_Char * const opt_plugin_atts[] = { "name", "version", "provider-name", NULL };
534 static const XML_Char * const req_bwcompatibility_atts[] = { NULL };
535 static const XML_Char * const opt_bwcompatibility_atts[] = { "abi", "api", NULL };
536 static const XML_Char * const req_cpluff_atts[] = { "version", NULL };
537 static const XML_Char * const opt_cpluff_atts[] = { NULL };
538 static const XML_Char * const req_import_atts[] = { "plugin", NULL };
539 static const XML_Char * const opt_import_atts[] = { "version", "optional", NULL };
540 static const XML_Char * const req_runtime_atts[] = { "library", NULL };
541 static const XML_Char * const opt_runtime_atts[] = { "funcs", NULL };
542 static const XML_Char * const req_ext_point_atts[] = { "id", NULL };
543 static const XML_Char * const opt_ext_point_atts[] = { "name", "schema", NULL };
544 static const XML_Char * const req_extension_atts[] = { "point", NULL };
545 //static const XML_Char * const opt_extension_atts[] = { "id", "name", NULL };
546 ploader_context_t *plcontext = userData;
549 // Process element start
550 switch (plcontext->state) {
553 if (!strcmp(name, "plugin")) {
554 plcontext->state = PARSER_PLUGIN;
555 if (!check_attributes(plcontext, name, atts,
556 req_plugin_atts, opt_plugin_atts)) {
559 for (i = 0; atts[i] != NULL; i += 2) {
560 if (!strcmp(atts[i], "name")) {
561 plcontext->plugin->name
562 = parser_strdup(plcontext, atts[i+1]);
563 } else if (!strcmp(atts[i], "id")) {
564 plcontext->plugin->identifier
565 = parser_strdup(plcontext, atts[i+1]);
566 } else if (!strcmp(atts[i], "version")) {
567 plcontext->plugin->version
568 = parser_strdup(plcontext, atts[i+1]);
569 } else if (!strcmp(atts[i], "provider-name")) {
570 plcontext->plugin->provider_name
571 = parser_strdup(plcontext, atts[i+1]);
572 } else if(!strcmp(atts[i],"url")){
573 plcontext->plugin->url = parser_strdup(plcontext, atts[i+1]);
574 } else if(!strcmp(atts[i],"resourcetype")){
575 plcontext->plugin->resourcetype = parser_strdup(plcontext, atts[i+1]);
579 unexpected_element(plcontext, name);
584 if (!strcmp(name, "backwards-compatibility")) {
585 if (check_attributes(plcontext, name, atts,
586 req_bwcompatibility_atts, opt_bwcompatibility_atts)) {
587 for (i = 0; atts[i] != NULL; i += 2) {
588 if (!strcmp(atts[i], "abi")) {
589 plcontext->plugin->abi_bw_compatibility = parser_strdup(plcontext, atts[i+1]);
590 } else if (!strcmp(atts[i], "api")) {
591 plcontext->plugin->api_bw_compatibility = parser_strdup(plcontext, atts[i+1]);
595 } else if (!strcmp(name, "requires")) {
596 plcontext->state = PARSER_REQUIRES;
597 } else if (!strcmp(name, "runtime")) {
598 if (check_attributes(plcontext, name, atts,
599 req_runtime_atts, opt_runtime_atts)) {
600 for (i = 0; atts[i] != NULL; i += 2) {
601 if (!strcmp(atts[i], "library")) {
602 plcontext->plugin->runtime_lib_name
603 = parser_strdup(plcontext, atts[i+1]);
604 } else if (!strcmp(atts[i], "funcs")) {
605 plcontext->plugin->runtime_funcs_symbol
606 = parser_strdup(plcontext, atts[i+1]);
610 } else if (!strcmp(name, "extension-point")) {
611 if (check_attributes(plcontext, name, atts,
612 req_ext_point_atts, opt_ext_point_atts)) {
613 cp_ext_point_t *ext_point;
615 // Allocate space for extension points, if necessary
616 if (plcontext->plugin->num_ext_points == plcontext->ext_points_size) {
620 if (plcontext->ext_points_size == 0) {
623 ns = plcontext->ext_points_size * 2;
625 if ((nep = realloc(plcontext->plugin->ext_points,
626 ns * sizeof(cp_ext_point_t))) == NULL) {
627 resource_error(plcontext);
630 plcontext->plugin->ext_points = nep;
631 plcontext->ext_points_size = ns;
634 // Parse extension point specification
635 ext_point = plcontext->plugin->ext_points
636 + plcontext->plugin->num_ext_points;
637 memset(ext_point, 0, sizeof(cp_ext_point_t));
638 ext_point->plugin = plcontext->plugin;
639 ext_point->name = NULL;
640 ext_point->local_id = NULL;
641 ext_point->identifier = NULL;
642 ext_point->schema_path = NULL;
643 for (i = 0; atts[i] != NULL; i += 2) {
644 if (!strcmp(atts[i], "name")) {
646 = parser_strdup(plcontext, atts[i+1]);
647 } else if (!strcmp(atts[i], "id")) {
649 = parser_strdup(plcontext, atts[i+1]);
650 ext_point->identifier
651 = parser_strscat(plcontext,
652 plcontext->plugin->identifier, ".", atts[i+1], NULL);
653 } else if (!strcmp(atts[i], "schema")) {
654 ext_point->schema_path
655 = parser_strdup(plcontext, atts[i+1]);
658 plcontext->plugin->num_ext_points++;
661 } else if (!(strcmp(name, "extension"))) {
662 plcontext->state = PARSER_EXTENSION;
663 plcontext->depth = 0;
664 if (check_req_attributes(
665 plcontext, name, atts, req_extension_atts)) {
666 cp_extension_t *extension;
668 // Allocate space for extensions, if necessary
669 if (plcontext->plugin->num_extensions == plcontext->extensions_size) {
673 if (plcontext->extensions_size == 0) {
676 ns = plcontext->extensions_size * 2;
678 if ((ne = realloc(plcontext->plugin->extensions,
679 ns * sizeof(cp_extension_t))) == NULL) {
680 resource_error(plcontext);
683 plcontext->plugin->extensions = ne;
684 plcontext->extensions_size = ns;
687 // Parse extension attributes
688 extension = plcontext->plugin->extensions
689 + plcontext->plugin->num_extensions;
690 memset(extension, 0, sizeof(cp_extension_t));
691 extension->plugin = plcontext->plugin;
692 extension->name = NULL;
693 extension->local_id = NULL;
694 extension->identifier = NULL;
695 extension->ext_point_id = NULL;
696 extension->configuration = NULL;
697 for (i = 0; atts[i] != NULL; i += 2) {
698 if (!strcmp(atts[i], "point")) {
699 extension->ext_point_id
700 = parser_strdup(plcontext, atts[i+1]);
701 } else if (!strcmp(atts[i], "id")) {
703 = parser_strdup(plcontext, atts[i+1]);
704 extension->identifier
705 = parser_strscat(plcontext,
706 plcontext->plugin->identifier, ".", atts[i+1], NULL);
707 } else if (!strcmp(atts[i], "name")) {
709 = parser_strdup(plcontext, atts[i+1]);
712 plcontext->plugin->num_extensions++;
714 // Initialize configuration parsing
715 if ((extension->configuration = plcontext->configuration
716 = parser_malloc(plcontext, sizeof(cp_cfg_element_t))) != NULL) {
717 init_cfg_element(plcontext, plcontext->configuration, name, atts, NULL);
719 XML_SetCharacterDataHandler(plcontext->parser, character_data_handler);
722 unexpected_element(plcontext, name);
726 case PARSER_REQUIRES:
727 if (!strcmp(name, "c-pluff")) {
728 if (check_attributes(plcontext, name, atts,
729 req_cpluff_atts, opt_cpluff_atts)) {
730 for (i = 0; atts[i] != NULL; i += 2) {
731 if (!strcmp(atts[i], "version")) {
732 plcontext->plugin->req_cpluff_version = parser_strdup(plcontext, atts[i+1]);
736 } else if (!strcmp(name, "import")) {
737 if (check_attributes(plcontext, name, atts,
738 req_import_atts, opt_import_atts)) {
739 cp_plugin_import_t *import = NULL;
741 // Allocate space for imports, if necessary
742 if (plcontext->plugin->num_imports == plcontext->imports_size) {
743 cp_plugin_import_t *ni;
746 if (plcontext->imports_size == 0) {
749 ns = plcontext->imports_size * 2;
751 if ((ni = realloc(plcontext->plugin->imports,
752 ns * sizeof(cp_plugin_import_t))) == NULL) {
753 resource_error(plcontext);
756 plcontext->plugin->imports = ni;
757 plcontext->imports_size = ns;
760 // Parse import specification
761 import = plcontext->plugin->imports
762 + plcontext->plugin->num_imports;
763 memset(import, 0, sizeof(cp_plugin_import_t));
764 import->plugin_id = NULL;
765 import->version = NULL;
766 for (i = 0; atts[i] != NULL; i += 2) {
767 if (!strcmp(atts[i], "plugin")) {
769 = parser_strdup(plcontext, atts[i+1]);
770 } else if (!strcmp(atts[i], "version")) {
771 import->version = parser_strdup(plcontext, atts[i+1]);
772 } else if (!strcmp(atts[i], "optional")) {
773 if (!strcmp(atts[i+1], "true")
774 || !strcmp(atts[i+1], "1")) {
775 import->optional = 1;
776 } else if (strcmp(atts[i+1], "false")
777 && strcmp(atts[i+1], "0")) {
778 descriptor_errorf(plcontext, 0, _("unknown boolean value: %s"), atts[i+1]);
782 plcontext->plugin->num_imports++;
785 unexpected_element(plcontext, name);
789 case PARSER_EXTENSION:
791 if (plcontext->configuration != NULL && plcontext->skippedCEs == 0) {
792 cp_cfg_element_t *ce;
794 // Allocate more space for children, if necessary
795 if (plcontext->configuration->num_children == plcontext->configuration->index) {
796 cp_cfg_element_t *nce;
799 if (plcontext->configuration->index == 0) {
802 ns = plcontext->configuration->index * 2;
804 if ((nce = realloc(plcontext->configuration->children,
805 ns * sizeof(cp_cfg_element_t))) == NULL) {
806 plcontext->skippedCEs++;
807 resource_error(plcontext);
810 plcontext->configuration->children = nce;
811 plcontext->configuration->index = ns;
814 // Save possible value
815 if (plcontext->value != NULL) {
816 plcontext->value[plcontext->value_length] = '\0';
817 plcontext->configuration->value = plcontext->value;
820 ce = plcontext->configuration->children + plcontext->configuration->num_children;
821 init_cfg_element(plcontext, ce, name, atts, plcontext->configuration);
822 plcontext->configuration->num_children++;
823 plcontext->configuration = ce;
831 unexpected_element(plcontext, name);
837 * Processes the end of element events while parsing.
839 * @param context the parsing context
840 * @param name the element name
842 static void CP_XMLCALL end_element_handler(
843 void *userData, const XML_Char *name) {
844 ploader_context_t *plcontext = userData;
846 // Process element end
847 switch (plcontext->state) {
850 if (!strcmp(name, "plugin")) {
852 // Readjust memory allocated for extension points, if necessary
853 if (plcontext->ext_points_size != plcontext->plugin->num_ext_points) {
856 if ((nep = realloc(plcontext->plugin->ext_points,
857 plcontext->plugin->num_ext_points *
858 sizeof(cp_ext_point_t))) != NULL
859 || plcontext->plugin->num_ext_points == 0) {
860 plcontext->plugin->ext_points = nep;
861 plcontext->ext_points_size = plcontext->plugin->num_ext_points;
865 // Readjust memory allocated for extensions, if necessary
866 if (plcontext->extensions_size != plcontext->plugin->num_extensions) {
869 if ((ne = realloc(plcontext->plugin->extensions,
870 plcontext->plugin->num_extensions *
871 sizeof(cp_extension_t))) != NULL
872 || plcontext->plugin->num_extensions == 0) {
873 plcontext->plugin->extensions = ne;
874 plcontext->extensions_size = plcontext->plugin->num_extensions;
878 plcontext->state = PARSER_END;
882 case PARSER_REQUIRES:
883 if (!strcmp(name, "requires")) {
885 // Readjust memory allocated for imports, if necessary
886 if (plcontext->imports_size != plcontext->plugin->num_imports) {
887 cp_plugin_import_t *ni;
889 if ((ni = realloc(plcontext->plugin->imports,
890 plcontext->plugin->num_imports *
891 sizeof(cp_plugin_import_t))) != NULL
892 || plcontext->plugin->num_imports == 0) {
893 plcontext->plugin->imports = ni;
894 plcontext->imports_size = plcontext->plugin->num_imports;
898 plcontext->state = PARSER_PLUGIN;
903 if (plcontext->depth-- == 0) {
904 plcontext->state = plcontext->saved_state;
908 case PARSER_EXTENSION:
909 if (plcontext->skippedCEs > 0) {
910 plcontext->skippedCEs--;
911 } else if (plcontext->configuration != NULL) {
913 // Readjust memory allocated for children, if necessary
914 if (plcontext->configuration->index != plcontext->configuration->num_children) {
915 cp_cfg_element_t *nce;
917 if ((nce = realloc(plcontext->configuration->children,
918 plcontext->configuration->num_children *
919 sizeof(cp_cfg_element_t))) != NULL
920 || plcontext->configuration->num_children == 0) {
921 plcontext->configuration->children = nce;
925 if (plcontext->configuration->parent != NULL) {
926 plcontext->configuration->index = plcontext->configuration->parent->num_children - 1;
928 plcontext->configuration->index = 0;
930 if (plcontext->value != NULL) {
931 char *v = plcontext->value;
934 // Ignore trailing whitespace
935 for (i = plcontext->value_length - 1; i >= 0; i--) {
936 if (v[i] != ' ' && v[i] != '\n' && v[i] != '\r' && v[i] != '\t') {
941 free(plcontext->value);
942 plcontext->value = NULL;
943 plcontext->value_length = 0;
944 plcontext->value_size = 0;
946 plcontext->value_length = i + 1;
949 if (plcontext->value != NULL) {
951 // Readjust memory allocated for value, if necessary
952 if (plcontext->value_size > plcontext->value_length + 1) {
955 if ((nv = realloc(plcontext->value, (plcontext->value_length + 1) * sizeof(char))) != NULL) {
956 plcontext->value = nv;
960 plcontext->value[plcontext->value_length] = '\0';
961 plcontext->configuration->value = plcontext->value;
962 plcontext->value = NULL;
963 plcontext->value_size = 0;
964 plcontext->value_length = 0;
966 plcontext->configuration = plcontext->configuration->parent;
968 // Restore possible value
969 if (plcontext->configuration != NULL
970 && plcontext->configuration->value != NULL) {
971 plcontext->value = plcontext->configuration->value;
972 plcontext->value_length = strlen(plcontext->value);
973 plcontext->value_size = CP_CFG_ELEMENT_VALUE_INITSIZE;
974 while (plcontext->value_size < plcontext->value_length + 1) {
975 plcontext->value_size *= 2;
980 if (plcontext->depth-- == 0) {
981 assert(!strcmp(name, "extension"));
982 plcontext->state = PARSER_PLUGIN;
983 XML_SetCharacterDataHandler(plcontext->parser, NULL);
988 descriptor_errorf(plcontext, 0, _("unexpected closing tag for %s"),
994 static void dealloc_plugin_info(cp_context_t *ctx, cp_plugin_info_t *plugin) {
995 cpi_free_plugin(plugin);
998 CP_C_API cp_plugin_info_t * cp_load_plugin_descriptor(cp_context_t *context, const char *path, cp_status_t *error) {
1000 cp_status_t status = CP_OK;
1002 XML_Parser parser = NULL;
1003 ploader_context_t *plcontext = NULL;
1004 cp_plugin_info_t *plugin = NULL;
1006 CHECK_NOT_NULL(context);
1007 CHECK_NOT_NULL(path);
1008 cpi_lock_context(context);
1009 cpi_check_invocation(context, CPI_CF_ANY, __func__);
1013 // Construct the file name for the plug-in descriptor
1014 path_len = strlen(path);
1015 if (path_len == 0) {
1019 if (path[path_len - 1] == CP_FNAMESEP_CHAR) {
1022 file = malloc((path_len + strlen(CP_PLUGIN_DESCRIPTOR) + 2) * sizeof(char));
1024 status = CP_ERR_RESOURCE;
1028 file[path_len] = CP_FNAMESEP_CHAR;
1029 strcpy(file + path_len + 1, CP_PLUGIN_DESCRIPTOR);
1032 if ((fh = fopen(file, "rb")) == NULL) {
1037 // Initialize the XML parsing
1038 parser = XML_ParserCreate(NULL);
1039 if (parser == NULL) {
1040 status = CP_ERR_RESOURCE;
1043 XML_SetElementHandler(parser,
1044 start_element_handler,
1045 end_element_handler);
1047 // Initialize the parsing context
1048 if ((plcontext = malloc(sizeof(ploader_context_t))) == NULL) {
1049 status = CP_ERR_RESOURCE;
1052 memset(plcontext, 0, sizeof(ploader_context_t));
1053 if ((plcontext->plugin = malloc(sizeof(cp_plugin_info_t))) == NULL) {
1054 status = CP_ERR_RESOURCE;
1057 plcontext->context = context;
1058 plcontext->configuration = NULL;
1059 plcontext->value = NULL;
1060 plcontext->parser = parser;
1061 plcontext->file = file;
1062 plcontext->state = PARSER_BEGIN;
1063 memset(plcontext->plugin, 0, sizeof(cp_plugin_info_t));
1064 plcontext->plugin->name = NULL;
1065 plcontext->plugin->identifier = NULL;
1066 plcontext->plugin->version = NULL;
1067 plcontext->plugin->provider_name = NULL;
1068 plcontext->plugin->abi_bw_compatibility = NULL;
1069 plcontext->plugin->api_bw_compatibility = NULL;
1070 plcontext->plugin->plugin_path = NULL;
1071 plcontext->plugin->req_cpluff_version = NULL;
1072 plcontext->plugin->imports = NULL;
1073 plcontext->plugin->runtime_lib_name = NULL;
1074 plcontext->plugin->runtime_funcs_symbol = NULL;
1075 plcontext->plugin->ext_points = NULL;
1076 plcontext->plugin->extensions = NULL;
1077 plcontext->plugin->url = NULL;
1078 plcontext->plugin->resourcetype = NULL;
1079 XML_SetUserData(parser, plcontext);
1081 // Parse the plug-in descriptor
1087 // Get buffer from Expat
1088 if ((xml_buffer = XML_GetBuffer(parser, CP_XML_PARSER_BUFFER_SIZE))
1090 status = CP_ERR_RESOURCE;
1094 // Read data into buffer
1095 bytes_read = fread(xml_buffer, 1, CP_XML_PARSER_BUFFER_SIZE, fh);
1102 if (!(i = XML_ParseBuffer(parser, bytes_read, bytes_read == 0))
1103 && context != NULL) {
1104 cpi_lock_context(context);
1106 N_("XML parsing error in %s, line %d, column %d (%s)."),
1108 XML_GetErrorLineNumber(parser),
1109 XML_GetErrorColumnNumber(parser) + 1,
1110 XML_ErrorString(XML_GetErrorCode(parser)));
1111 cpi_unlock_context(context);
1113 if (!i || plcontext->state == PARSER_ERROR) {
1114 status = CP_ERR_MALFORMED;
1118 if (bytes_read == 0) {
1122 if (status == CP_OK) {
1123 if (plcontext->state != PARSER_END || plcontext->error_count > 0) {
1124 status = CP_ERR_MALFORMED;
1126 if (plcontext->resource_error_count > 0) {
1127 status = CP_ERR_RESOURCE;
1130 if (status != CP_OK) {
1134 // Initialize the plug-in path
1135 *(file + path_len) = '\0';
1136 plcontext->plugin->plugin_path = file;
1139 // Increase plug-in usage count
1140 if ((status = cpi_register_info(context, plcontext->plugin, (void (*)(cp_context_t *, void *)) dealloc_plugin_info)) != CP_OK) {
1146 // Report possible errors
1147 if (status != CP_OK) {
1149 case CP_ERR_MALFORMED:
1151 N_("Plug-in descriptor in %s is invalid."), path);
1155 N_("An I/O error occurred while loading a plug-in descriptor from %s."), path);
1157 case CP_ERR_RESOURCE:
1159 N_("Insufficient system resources to load a plug-in descriptor from %s."), path);
1163 N_("Failed to load a plug-in descriptor from %s."), path);
1167 cpi_unlock_context(context);
1169 // Release persistently allocated data on failure
1170 if (status != CP_OK) {
1175 if (plcontext != NULL && plcontext->plugin != NULL) {
1176 cpi_free_plugin(plcontext->plugin);
1177 plcontext->plugin = NULL;
1181 // Otherwise copy the plug-in pointer
1183 plugin = plcontext->plugin;
1186 // Release data allocated for parsing
1187 if (parser != NULL) {
1188 XML_ParserFree(parser);
1193 if (plcontext != NULL) {
1194 if (plcontext->value != NULL) {
1195 free(plcontext->value);
1201 // Return error code
1202 if (error != NULL) {