Beispiel #1
0
CP_C_API cp_status_t cp_define_symbol(cp_context_t *context, const char *name, void *ptr) {
    cp_status_t status = CP_OK;

    CHECK_NOT_NULL(context);
    CHECK_NOT_NULL(name);
    CHECK_NOT_NULL(ptr);
    if (context->plugin == NULL) {
        cpi_fatalf(_("Only plug-ins can define context specific symbols."));
    }

    cpi_lock_context(context);
    cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER, __func__);
    do {
        char *n;

        // Create a symbol hash if necessary
        if (context->plugin->defined_symbols == NULL) {
            if ((context->plugin->defined_symbols = hash_create(HASHCOUNT_T_MAX, (int (*)(const void *, const void *)) strcmp, NULL)) == NULL) {
                status = CP_ERR_RESOURCE;
                break;
            }
        }

        // Check for a previously defined symbol
        if (hash_lookup(context->plugin->defined_symbols, name) != NULL) {
            status = CP_ERR_CONFLICT;
            break;
        }

        // Insert the symbol into the symbol hash
        n = strdup(name);
        if (n == NULL || !hash_alloc_insert(context->plugin->defined_symbols, n, ptr)) {
            free(n);
            status = CP_ERR_RESOURCE;
            break;
        }

    } while (0);

    // Report error
    if (status != CP_OK) {
        switch (status) {
        case CP_ERR_RESOURCE:
            cpi_errorf(context, N_("Plug-in %s could not define symbol %s due to insufficient memory."), context->plugin->plugin->identifier, name);
            break;
        case CP_ERR_CONFLICT:
            cpi_errorf(context, N_("Plug-in %s tried to redefine symbol %s."), context->plugin->plugin->identifier, name);
            break;
        default:
            break;
        }
    }
    cpi_unlock_context(context);

    return status;
}
Beispiel #2
0
CP_C_API cp_status_t cp_register_pcollection(cp_context_t *context, const char *dir) {
	cp_status_t status = CP_OK;
	
	CHECK_NOT_NULL(context);
	CHECK_NOT_NULL(dir);
	
	cpi_lock_context(context);
	cpi_check_invocation(context, CPI_CF_ANY, __func__);
	do {
	
		// Initialize plug-in loader
		if ((status = init_local_ploader(context)) != CP_OK) {
			break;
		}
		
		// Register directory
		status = cp_lpl_register_dir(context->env->local_loader, dir);
	
	} while (0);

	// Report error or success
	if (status != CP_OK) {
		cpi_errorf(context, N_("The plug-in collection in path %s could not be registered due to insufficient memory."), dir);
	} else {
		cpi_debugf(context, N_("The plug-in collection in path %s was registered."), dir);
	}
	cpi_unlock_context(context);

	return status;
}
Beispiel #3
0
/**
 * Reports a descriptor error. Does not set the parser to error state but
 * increments the error count, unless this is merely a warning.
 * 
 * @param context the parsing context
 * @param warn whether this is only a warning
 * @param error_msg the error message
 * @param ... parameters for the error message
 */
static void descriptor_errorf(ploader_context_t *plcontext, int warn,
	const char *error_msg, ...) {
	va_list ap;
	char message[128];

	va_start(ap, error_msg);
	vsnprintf(message, sizeof(message), error_msg, ap);
	va_end(ap);
	message[127] = '\0';
	if (warn) {
		cpi_warnf(plcontext->context,
			N_("Suspicious plug-in descriptor content in %s, line %d, column %d (%s)."),
		plcontext->file,
		XML_GetCurrentLineNumber(plcontext->parser),
		XML_GetCurrentColumnNumber(plcontext->parser) + 1,
		message);
	} else {				
		cpi_errorf(plcontext->context,
			N_("Invalid plug-in descriptor content in %s, line %d, column %d (%s)."),
			plcontext->file,
			XML_GetCurrentLineNumber(plcontext->parser),
			XML_GetCurrentColumnNumber(plcontext->parser) + 1,
			message);
	}
	if (!warn) {
		plcontext->error_count++;
	}
}
Beispiel #4
0
/**
 * Reports insufficient system resources while parsing and increments the
 * resource error count.
 * 
 * @param context the parsing context
 */
static void resource_error(ploader_context_t *plcontext) {
	if (plcontext->resource_error_count == 0) {
		cpi_errorf(plcontext->context,
			N_("Insufficient system resources to parse plug-in descriptor content in %s, line %d, column %d."),
			plcontext->file,
			XML_GetCurrentLineNumber(plcontext->parser),
			XML_GetCurrentColumnNumber(plcontext->parser) + 1);
	}
	plcontext->resource_error_count++;
}
Beispiel #5
0
CP_C_API void cp_release_symbol(cp_context_t *context, const void *ptr) {
    hnode_t *node;
    symbol_info_t *symbol_info;
    symbol_provider_info_t *provider_info;

    CHECK_NOT_NULL(context);
    CHECK_NOT_NULL(ptr);

    cpi_lock_context(context);
    cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER, __func__);
    do {

        // Look up the symbol
        if ((node = hash_lookup(context->resolved_symbols, ptr)) == NULL) {
            cpi_errorf(context, N_("Could not release unknown symbol at address %p."), ptr);
            break;
        }
        symbol_info = hnode_get(node);
        provider_info = symbol_info->provider_info;

        // Decrease usage count
        assert(symbol_info->usage_count > 0);
        symbol_info->usage_count--;
        assert(provider_info->usage_count > 0);
        provider_info->usage_count--;

        // Check if the symbol is not being used anymore
        if (symbol_info->usage_count == 0) {
            hash_delete_free(context->resolved_symbols, node);
            free(symbol_info);
            if (cpi_is_logged(context, CP_LOG_DEBUG)) {
                char owner[64];
                /* TRANSLATORS: First %s is the context owner */
                cpi_debugf(context, _("%s released the symbol at address %p defined by plug-in %s."), cpi_context_owner(context, owner, sizeof(owner)), ptr, provider_info->plugin->plugin->identifier);
            }
        }

        // Check if the symbol providing plug-in is not being used anymore
        if (provider_info->usage_count == 0) {
            node = hash_lookup(context->symbol_providers, provider_info->plugin);
            assert(node != NULL);
            hash_delete_free(context->symbol_providers, node);
            if (!provider_info->imported) {
                cpi_ptrset_remove(context->plugin->imported, provider_info->plugin);
                cpi_ptrset_remove(provider_info->plugin->importing, context->plugin);
                cpi_debugf(context, _("A dynamic dependency from plug-in %s to plug-in %s was removed."), context->plugin->plugin->identifier, provider_info->plugin->plugin->identifier);
            }
            free(provider_info);
        }

    } while (0);
    cpi_unlock_context(context);
}
Beispiel #6
0
CP_C_API cp_status_t cp_register_ploader(cp_context_t *ctx, cp_plugin_loader_t *loader) {
	cp_status_t status = CP_OK;
	hash_t *loader_plugins = NULL;
	
	CHECK_NOT_NULL(ctx);
	CHECK_NOT_NULL(loader);
	
	cpi_lock_context(ctx);
	cpi_check_invocation(ctx, CPI_CF_ANY, __func__);
	do {
		if ((loader_plugins = hash_create(HASHCOUNT_T_MAX, (int (*)(const void *, const void *)) strcmp, NULL)) == NULL) {
			status = CP_ERR_RESOURCE;
			break;
		}
		if (!hash_alloc_insert(ctx->env->loaders_to_plugins, loader, loader_plugins)) {
			status = CP_ERR_RESOURCE;
			break;
		}
	} while (0);
	
	// Report error or success
	if (status != CP_OK) {
		cpi_errorf(ctx, N_("The plug-in loader %p could not be registered due to insufficient memory."), (void *) loader);
	} else {
		cpi_debugf(ctx, N_("The plug-in loader %p was registered."), (void *) loader);
	}
	cpi_unlock_context(ctx);
	
	// Release resources
	if (status != CP_OK) {
		if (loader_plugins != NULL) {
			assert(hash_isempty(loader_plugins));
			hash_destroy(loader_plugins);
		}
	}
	
	return status;
}
Beispiel #7
0
CP_C_API void * cp_resolve_symbol(cp_context_t *context, const char *id, const char *name, cp_status_t *error) {
    cp_status_t status = CP_OK;
    int error_reported = 1;
    hnode_t *node;
    void *symbol = NULL;
    symbol_info_t *symbol_info = NULL;
    symbol_provider_info_t *provider_info = NULL;
    cp_plugin_t *pp = NULL;

    CHECK_NOT_NULL(context);
    CHECK_NOT_NULL(id);
    CHECK_NOT_NULL(name);

    // Resolve the symbol
    cpi_lock_context(context);
    cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER | CPI_CF_STOP, __func__);
    do {

        // Allocate space for symbol hashes, if necessary
        if (context->resolved_symbols == NULL) {
            context->resolved_symbols = hash_create(HASHCOUNT_T_MAX, cpi_comp_ptr, cpi_hashfunc_ptr);
        }
        if (context->symbol_providers == NULL) {
            context->symbol_providers = hash_create(HASHCOUNT_T_MAX, cpi_comp_ptr, cpi_hashfunc_ptr);
        }
        if (context->resolved_symbols == NULL
                || context->symbol_providers == NULL) {
            status = CP_ERR_RESOURCE;
            break;
        }

        // Look up the symbol defining plug-in
        node = hash_lookup(context->env->plugins, id);
        if (node == NULL) {
            cpi_warnf(context, N_("Symbol %s in unknown plug-in %s could not be resolved."), name, id);
            status = CP_ERR_UNKNOWN;
            break;
        }
        pp = hnode_get(node);

        // Make sure the plug-in has been started
        if ((status = cpi_start_plugin(context, pp)) != CP_OK) {
            printf("Symbol %s in plug-in %s could not be resolved because the plug-in could not be started.\n", name, id);
            cpi_errorf(context, N_("Symbol %s in plug-in %s could not be resolved because the plug-in could not be started."), name, id);
            error_reported = 1;
            break;
        }

        // Check for a context specific symbol
        if (pp->defined_symbols != NULL && (node = hash_lookup(pp->defined_symbols, name)) != NULL) {
            symbol = hnode_get(node);
        }

        // Fall back to global symbols, if necessary
        if (symbol == NULL && pp->runtime_lib != NULL) {
            symbol = DLSYM(pp->runtime_lib, name);
        }
        if (symbol == NULL) {
            const char *error = DLERROR();
            if (error == NULL) {
                error = _("Unspecified error.");
            }
            cpi_warnf(context, N_("Symbol %s in plug-in %s could not be resolved: %s"), name, id, error);
            status = CP_ERR_UNKNOWN;
            break;
        }

        // Lookup or initialize symbol provider information
        if ((node = hash_lookup(context->symbol_providers, pp)) != NULL) {
            provider_info = hnode_get(node);
        } else {
            if ((provider_info = malloc(sizeof(symbol_provider_info_t))) == NULL) {
                status = CP_ERR_RESOURCE;
                break;
            }
            memset(provider_info, 0, sizeof(symbol_provider_info_t));
            provider_info->plugin = pp;
            provider_info->imported = (context->plugin == NULL || cpi_ptrset_contains(context->plugin->imported, pp));
            if (!hash_alloc_insert(context->symbol_providers, pp, provider_info)) {
                status = CP_ERR_RESOURCE;
                break;
            }
        }

        // Lookup or initialize symbol information
        if ((node = hash_lookup(context->resolved_symbols, symbol)) != NULL) {
            symbol_info = hnode_get(node);
        } else {
            if ((symbol_info = malloc(sizeof(symbol_info_t))) == NULL) {
                status = CP_ERR_RESOURCE;
                break;
            }
            memset(symbol_info, 0, sizeof(symbol_info_t));
            symbol_info->provider_info = provider_info;
            if (!hash_alloc_insert(context->resolved_symbols, symbol, symbol_info)) {
                status = CP_ERR_RESOURCE;
                break;
            }
        }

        // Add dependencies (for plug-in)
        if (provider_info != NULL
                && !provider_info->imported
                && provider_info->usage_count == 0) {
            if (!cpi_ptrset_add(context->plugin->imported, pp)) {
                status = CP_ERR_RESOURCE;
                break;
            }
            if (!cpi_ptrset_add(pp->importing, context->plugin)) {
                cpi_ptrset_remove(context->plugin->imported, pp);
                status = CP_ERR_RESOURCE;
                break;
            }
            cpi_debugf(context, "A dynamic dependency was created from plug-in %s to plug-in %s.", context->plugin->plugin->identifier, pp->plugin->identifier);
        }

        // Increase usage counts
        symbol_info->usage_count++;
        provider_info->usage_count++;

        if (cpi_is_logged(context, CP_LOG_DEBUG)) {
            char owner[64];
            /* TRANSLATORS: First %s is the context owner */
            cpi_debugf(context, "%s resolved symbol %s defined by plug-in %s.", cpi_context_owner(context, owner, sizeof(owner)), name, id);
        }
    } while (0);

    // Clean up
    if (symbol_info != NULL && symbol_info->usage_count == 0) {
        if ((node = hash_lookup(context->resolved_symbols, symbol)) != NULL) {
            hash_delete_free(context->resolved_symbols, node);
        }
        free(symbol_info);
    }
    if (provider_info != NULL && provider_info->usage_count == 0) {
        if ((node = hash_lookup(context->symbol_providers, pp)) != NULL) {
            hash_delete_free(context->symbol_providers, node);
        }
        free(provider_info);
    }

    // Report insufficient memory error
    if (status == CP_ERR_RESOURCE && !error_reported) {
        cpi_errorf(context, N_("Symbol %s in plug-in %s could not be resolved due to insufficient memory."), name, id);
    }
    cpi_unlock_context(context);

    // Return error code
    if (error != NULL) {
        *error = status;
    }

    // Return symbol
    return symbol;
}
Beispiel #8
0
CP_C_API cp_status_t cp_scan_plugins(cp_context_t *context, int flags) {
	hash_t *avail_plugins = NULL;
	list_t *started_plugins = NULL;
	cp_plugin_info_t **plugins = NULL;
	char *pdir_path = NULL;
	int pdir_path_size = 0;
	int plugins_stopped = 0;
	cp_status_t status = CP_OK;
	
	CHECK_NOT_NULL(context);
	
	cpi_lock_context(context);
	cpi_check_invocation(context, CPI_CF_ANY, __func__);
	cpi_debug(context, N_("Plug-in scan is starting."));
	do {
		lnode_t *lnode;
		hscan_t hscan;
		hnode_t *hnode;
	
		// Create a hash for available plug-ins 
		if ((avail_plugins = hash_create(HASHCOUNT_T_MAX, (int (*)(const void *, const void *)) strcmp, NULL)) == NULL) {
			status = CP_ERR_RESOURCE;
			break;
		}
	
		// Scan plug-in directories for available plug-ins 
		lnode = list_first(context->env->plugin_dirs);
		while (lnode != NULL) {
			const char *dir_path;
			DIR *dir;
			
			dir_path = lnode_get(lnode);
			dir = opendir(dir_path);
			if (dir != NULL) {
				int dir_path_len;
				struct dirent *de;
				
				dir_path_len = strlen(dir_path);
				if (dir_path[dir_path_len - 1] == CP_FNAMESEP_CHAR) {
					dir_path_len--;
				}
				errno = 0;
				while ((de = readdir(dir)) != NULL) {
					if (de->d_name[0] != '\0' && de->d_name[0] != '.') {
						int pdir_path_len = dir_path_len + 1 + strlen(de->d_name) + 1;
						cp_plugin_info_t *plugin;
						cp_status_t s;
						hnode_t *hnode;

						// Allocate memory for plug-in descriptor path 
						if (pdir_path_size <= pdir_path_len) {
							char *new_pdir_path;
						
							if (pdir_path_size == 0) {
								pdir_path_size = 128;
							}
							while (pdir_path_size <= pdir_path_len) {
								pdir_path_size *= 2;
							}
							new_pdir_path = realloc(pdir_path, pdir_path_size * sizeof(char));
							if (new_pdir_path == NULL) {
								cpi_errorf(context, N_("Could not check possible plug-in location %s%c%s due to insufficient system resources."), dir_path, CP_FNAMESEP_CHAR, de->d_name);
								status = CP_ERR_RESOURCE;
								// continue loading plug-ins from other directories 
								continue;
							}
							pdir_path = new_pdir_path;
						}
					
						// Construct plug-in descriptor path 
						strcpy(pdir_path, dir_path);
						pdir_path[dir_path_len] = CP_FNAMESEP_CHAR;
						strcpy(pdir_path + dir_path_len + 1, de->d_name);
							
						// Try to load a plug-in 
						plugin = cp_load_plugin_descriptor(context, pdir_path, &s);
						if (plugin == NULL) {
							status = s;
							// continue loading plug-ins from other directories 
							continue;
						}
					
						// Insert plug-in to the list of available plug-ins 
						if ((hnode = hash_lookup(avail_plugins, plugin->identifier)) != NULL) {
							cp_plugin_info_t *plugin2 = hnode_get(hnode);
							if (cpi_vercmp(plugin->version, plugin2->version) > 0) {
								hash_delete_free(avail_plugins, hnode);
								cp_release_info(context, plugin2);
								hnode = NULL;
							}
						}
						if (hnode == NULL) {
							if (!hash_alloc_insert(avail_plugins, plugin->identifier, plugin)) {
								cpi_errorf(context, N_("Plug-in %s version %s could not be loaded due to insufficient system resources."), plugin->identifier, plugin->version);
								cp_release_info(context, plugin);
								status = CP_ERR_RESOURCE;
								// continue loading plug-ins from other directories 
								continue;
							}
						}
						
					}
					errno = 0;
				}
				if (errno) {
					cpi_errorf(context, N_("Could not read plug-in directory %s: %s"), dir_path, strerror(errno));
					status = CP_ERR_IO;
					// continue loading plug-ins from other directories 
				}
				closedir(dir);
			} else {
				cpi_errorf(context, N_("Could not open plug-in directory %s: %s"), dir_path, strerror(errno));
				status = CP_ERR_IO;
				// continue loading plug-ins from other directories 
			}
			
			lnode = list_next(context->env->plugin_dirs, lnode);
		}
		
		// Copy the list of started plug-ins, if necessary 
		if ((flags & CP_SP_RESTART_ACTIVE)
			&& (flags & (CP_SP_UPGRADE | CP_SP_STOP_ALL_ON_INSTALL))) {
			int i;
			cp_status_t s;

			if ((plugins = cp_get_plugins_info(context, &s, NULL)) == NULL) {
				status = s;
				break;
			}
			if ((started_plugins = list_create(LISTCOUNT_T_MAX)) == NULL) {
				status = CP_ERR_RESOURCE;
				break;
			}
			for (i = 0; plugins[i] != NULL; i++) {
				cp_plugin_state_t state;
				
				state = cp_get_plugin_state(context, plugins[i]->identifier);
				if (state == CP_PLUGIN_STARTING || state == CP_PLUGIN_ACTIVE) {
					char *pid;
				
					if ((pid = strdup(plugins[i]->identifier)) == NULL) {
						status = CP_ERR_RESOURCE;
						break;
					}
					if ((lnode = lnode_create(pid)) == NULL) {
						free(pid);
						status = CP_ERR_RESOURCE;
						break;
					}
					list_append(started_plugins, lnode);
				}
			}
			cpi_release_info(context, plugins);
			plugins = NULL;
		}
		
		// Install/upgrade plug-ins 
		hash_scan_begin(&hscan, avail_plugins);
		while ((hnode = hash_scan_next(&hscan)) != NULL) {
			cp_plugin_info_t *plugin;
			cp_plugin_t *ip = NULL;
			hnode_t *hn2;
			int s;
			
			plugin = hnode_get(hnode);
			hn2 = hash_lookup(context->env->plugins, plugin->identifier);
			if (hn2 != NULL) {
				ip = hnode_get(hn2);
			}
			
			// Unload the installed plug-in if it is to be upgraded 
			if (ip != NULL
				&& (flags & CP_SP_UPGRADE)
				&& ((ip->plugin->version == NULL && plugin->version != NULL)
					|| (ip->plugin->version != NULL
						&& plugin->version != NULL
						&& cpi_vercmp(plugin->version, ip->plugin->version) > 0))) {
				if ((flags & (CP_SP_STOP_ALL_ON_UPGRADE | CP_SP_STOP_ALL_ON_INSTALL))
					&& !plugins_stopped) {
					plugins_stopped = 1;
					cp_stop_plugins(context);
				}
				s = cp_uninstall_plugin(context, plugin->identifier);
				assert(s == CP_OK);
				ip = NULL;
			}
			
			// Install the plug-in, if to be installed 
			if (ip == NULL) {
				if ((flags & CP_SP_STOP_ALL_ON_INSTALL) && !plugins_stopped) {
					plugins_stopped = 1;
					cp_stop_plugins(context);
				}
				if ((s = cp_install_plugin(context, plugin)) != CP_OK) {
					status = s;
					break;
				}
			}
			
			// Remove the plug-in from the hash
			hash_scan_delfree(avail_plugins, hnode);
			cp_release_info(context, plugin);
		}
		
		// Restart stopped plug-ins if necessary 
		if (started_plugins != NULL) {
			lnode = list_first(started_plugins);
			while (lnode != NULL) {
				char *pid;
				int s;
				
				pid = lnode_get(lnode);
				s = cp_start_plugin(context, pid);
				if (s != CP_OK) {
					status = s;
				}
				lnode = list_next(started_plugins, lnode);
			}
		}
		
	} while (0);

	// Report error
	switch (status) {
		case CP_OK:
			cpi_debug(context, N_("Plug-in scan has completed successfully."));
			break;
		case CP_ERR_RESOURCE:
			cpi_error(context, N_("Could not scan plug-ins due to insufficient system resources."));
			break;
		default:
			cpi_error(context, N_("Could not scan plug-ins."));
			break;
	}
	cpi_unlock_context(context);
	
	// Release resources 
	if (pdir_path != NULL) {
		free(pdir_path);
	}
	if (avail_plugins != NULL) {
		hscan_t hscan;
		hnode_t *hnode;
		
		hash_scan_begin(&hscan, avail_plugins);
		while ((hnode = hash_scan_next(&hscan)) != NULL) {
			cp_plugin_info_t *p = hnode_get(hnode);
			hash_scan_delfree(avail_plugins, hnode);
			cp_release_info(context, p);
		}
		hash_destroy(avail_plugins);
	}
	if (started_plugins != NULL) {
		list_process(started_plugins, NULL, cpi_process_free_ptr);
		list_destroy(started_plugins);
	}
	if (plugins != NULL) {
		cp_release_info(context, plugins);
	}

	return status;
}
Beispiel #9
0
CP_C_API cp_plugin_info_t * cp_load_plugin_descriptor(cp_context_t *context, const char *path, cp_status_t *error) {
	char *file = NULL;
	cp_status_t status = CP_OK;
	FILE *fh = NULL;
	XML_Parser parser = NULL;
	ploader_context_t *plcontext = NULL;
	cp_plugin_info_t *plugin = NULL;

	CHECK_NOT_NULL(context);
	CHECK_NOT_NULL(path);
	cpi_lock_context(context);
	cpi_check_invocation(context, CPI_CF_ANY, __func__);
	do {
		int path_len;

		// Construct the file name for the plug-in descriptor 
		path_len = strlen(path);
		if (path_len == 0) {
			status = CP_ERR_IO;
			break;
		}
		if (path[path_len - 1] == CP_FNAMESEP_CHAR) {
			path_len--;
		}
		file = malloc((path_len + strlen(CP_PLUGIN_DESCRIPTOR) + 2) * sizeof(char));
		if (file == NULL) {
			status = CP_ERR_RESOURCE;
			break;
		}
		strcpy(file, path);
		file[path_len] = CP_FNAMESEP_CHAR;
		strcpy(file + path_len + 1, CP_PLUGIN_DESCRIPTOR);

		// Open the file 
		if ((fh = fopen(file, "rb")) == NULL) {
			status = CP_ERR_IO;
			break;
		}

		// Initialize the XML parsing 
		parser = XML_ParserCreate(NULL);
		if (parser == NULL) {
			status = CP_ERR_RESOURCE;
			break;
		}
		XML_SetElementHandler(parser,
			start_element_handler,
			end_element_handler);
		
		// Initialize the parsing context 
		if ((plcontext = malloc(sizeof(ploader_context_t))) == NULL) {
			status = CP_ERR_RESOURCE;
			break;
		}
		memset(plcontext, 0, sizeof(ploader_context_t));
		if ((plcontext->plugin = malloc(sizeof(cp_plugin_info_t))) == NULL) {
			status = CP_ERR_RESOURCE;
			break;
		}
		plcontext->context = context;
		plcontext->configuration = NULL;
		plcontext->value = NULL;
		plcontext->parser = parser;
		plcontext->file = file;
		plcontext->state = PARSER_BEGIN;
		memset(plcontext->plugin, 0, sizeof(cp_plugin_info_t));
		plcontext->plugin->name = NULL;
		plcontext->plugin->identifier = NULL;
		plcontext->plugin->version = NULL;
		plcontext->plugin->provider_name = NULL;
		plcontext->plugin->abi_bw_compatibility = NULL;
		plcontext->plugin->api_bw_compatibility = NULL;
		plcontext->plugin->plugin_path = NULL;
		plcontext->plugin->req_cpluff_version = NULL;
		plcontext->plugin->imports = NULL;
		plcontext->plugin->runtime_lib_name = NULL;
		plcontext->plugin->runtime_funcs_symbol = NULL;
		plcontext->plugin->ext_points = NULL;
		plcontext->plugin->extensions = NULL;
		plcontext->plugin->url = NULL;
		plcontext->plugin->resourcetype = NULL;
		XML_SetUserData(parser, plcontext);

		// Parse the plug-in descriptor 
		while (1) {
			int bytes_read;
			void *xml_buffer;
			int i;
			
			// Get buffer from Expat 
			if ((xml_buffer = XML_GetBuffer(parser, CP_XML_PARSER_BUFFER_SIZE))
				== NULL) {
				status = CP_ERR_RESOURCE;
				break;
			}
			
			// Read data into buffer 
			bytes_read = fread(xml_buffer, 1, CP_XML_PARSER_BUFFER_SIZE, fh);
			if (ferror(fh)) {
				status = CP_ERR_IO;
				break;
			}

			// Parse the data 
			if (!(i = XML_ParseBuffer(parser, bytes_read, bytes_read == 0))
				&& context != NULL) {
				cpi_lock_context(context);
				cpi_errorf(context,
					N_("XML parsing error in %s, line %d, column %d (%s)."),
					file,
					XML_GetErrorLineNumber(parser),
					XML_GetErrorColumnNumber(parser) + 1,
					XML_ErrorString(XML_GetErrorCode(parser)));
				cpi_unlock_context(context);
			}
			if (!i || plcontext->state == PARSER_ERROR) {
				status = CP_ERR_MALFORMED;
				break;
			}
			
			if (bytes_read == 0) {
				break;
			}
		}
		if (status == CP_OK) {
			if (plcontext->state != PARSER_END || plcontext->error_count > 0) {
				status = CP_ERR_MALFORMED;
			}
			if (plcontext->resource_error_count > 0) {
				status = CP_ERR_RESOURCE;
			}
		}
		if (status != CP_OK) {
			break;
		}

		// Initialize the plug-in path 
		*(file + path_len) = '\0';
		plcontext->plugin->plugin_path = file;
		file = NULL;
		
		// Increase plug-in usage count
		if ((status = cpi_register_info(context, plcontext->plugin, (void (*)(cp_context_t *, void *)) dealloc_plugin_info)) != CP_OK) {
			break;
		}
		
	} while (0);

	// Report possible errors
	if (status != CP_OK) {
		switch (status) {
			case CP_ERR_MALFORMED:
				cpi_errorf(context,
					N_("Plug-in descriptor in %s is invalid."), path);
				break;
			case CP_ERR_IO:
				cpi_errorf(context,
					N_("An I/O error occurred while loading a plug-in descriptor from %s."), path);
				break;
			case CP_ERR_RESOURCE:
				cpi_errorf(context,
					N_("Insufficient system resources to load a plug-in descriptor from %s."), path);
				break;
			default:
				cpi_errorf(context,
					N_("Failed to load a plug-in descriptor from %s."), path);
				break;
		}
	}
	cpi_unlock_context(context);

	// Release persistently allocated data on failure 
	if (status != CP_OK) {
		if (file != NULL) {
			free(file);
			file = NULL;
		}
		if (plcontext != NULL && plcontext->plugin != NULL) {
			cpi_free_plugin(plcontext->plugin);
			plcontext->plugin = NULL;
		}
	}
	
	// Otherwise copy the plug-in pointer
	else {
		plugin = plcontext->plugin;
	}

	// Release data allocated for parsing 
	if (parser != NULL) {
		XML_ParserFree(parser);
	}
	if (fh != NULL) {
		fclose(fh);
	}
	if (plcontext != NULL) {
		if (plcontext->value != NULL) {
			free(plcontext->value);
		}
		free(plcontext);
		plcontext = NULL;
	}

	// Return error code
	if (error != NULL) {
		*error = status;
	}

	return plugin;
}