Example #1
0
/* Parse an or-expression string in *P.
   On success, return the parsed or-expression.
   On error, set *P->ERROR to an error string (for free()) or NULL, and return
   NULL. */
static struct expr *
parse_or(struct parsing *p)
{
	struct expr *res;

	res = parse_and(p);
	if (res == NULL)
		return NULL;
	while (p->token == EO_OR) {
		struct expr *e2, *e;

		if (lex(p) != 0)
			goto err_res;
		e2 = parse_and(p);
		if (e2 == NULL)
			goto err_res;
		e = parser_malloc(p, sizeof(*e));
		if (e == NULL) {
			expr_free(e2);
			goto err_res;
		}
		e->op = EO_OR;
		e->v.sub[0] = res;
		e->v.sub[1] = e2;
		res = e;
	}
	return res;

err_res:
	expr_free(res);
	return NULL;
}
Example #2
0
/**
 * Concatenates the specified strings into a new string. The memory for the concatenated
 * string is allocated using malloc. Reports a resource error if there is not
 * enough available memory.
 * 
 * @param context the parsing context
 * @param ... the strings to be concatenated, terminated by NULL
 * @return the concatenated string, or NULL if memory allocation failed
 */
static char *parser_strscat(ploader_context_t *plcontext, ...) {
	va_list ap;
	const char *str;
	char *dst;
	size_t len;
	
	// Calculate the length of the concatenated string 
	va_start(ap, plcontext);
	len = 0;
	while ((str = va_arg(ap, const char *)) != NULL) {
		len += strlen(str);
	}
	va_end(ap);
	
	// Allocate space for the concatenated string 
	if ((dst = parser_malloc(plcontext, sizeof(char) * (len + 1))) == NULL) {
		return NULL;
	}
	
	// Copy the strings 
	len = 0;
	va_start(ap, plcontext);
	while ((str = va_arg(ap, const char *)) != NULL) {
		strcpy(dst + len, str);
		len += strlen(str);
	}
	va_end(ap);
	dst[len] = '\0';
	return dst;
}
Example #3
0
/* Parse a \regexp comparison-expression string in *P, with \regexp parsed.
   Use or free EXPR.
   On success, return the parsed comparison-expression.
   On error, set *P->ERROR to an error string (for free()) or NULL, and return
   NULL. */
static struct expr *
parse_comparison_regexp(struct parsing *p, struct expr *res)
{
	int err;

	if (lex(p) != 0)
		goto err_res;
	if (p->token != T_STRING && p->token != T_REGEXP) {
		*p->error = NULL;
		asprintf(p->error, "Regexp expected, got `%.*s'", p->token_len,
			 p->token_start);
		goto err_res;
	}
	res->v.regexp = parser_malloc(p, sizeof(*res->v.regexp));
	if (res->v.regexp == NULL)
		goto err_res;
	err = regcomp(res->v.regexp, p->token_value, REG_EXTENDED | REG_NOSUB);
	if (err != 0) {
		size_t err_size;
		char *err_msg;

		err_size = regerror(err, res->v.regexp, NULL, 0);
		err_msg = parser_malloc(p, err_size);
		if (err_msg == NULL)
			goto err_res_regexp;
		regerror(err, res->v.regexp, err_msg, err_size);
		*p->error = NULL;
		asprintf(p->error, "Invalid regexp: %s", err_msg);
		free(err_msg);
		goto err_res_regexp;
	}
	res->op = EO_REGEXP_MATCHES;
	if (lex(p) != 0) {
		expr_free(res);
		return NULL;
	}
	return res;

err_res_regexp:
	free(res->v.regexp);
err_res:
	free(res);
	return NULL;
}
Example #4
0
/* Parse a primary-expression string in *P.
   On success, return the parsed primary-expression.
   On error, set *P->ERROR to an error string (for free()) or NULL, and return
   NULL. */
static struct expr *
parse_primary(struct parsing *p)
{
	struct expr *e;

	switch (p->token) {
	case EO_NOT: {
		struct expr *res;

		if (lex(p) != 0)
			return NULL;
		e = parse_primary(p);
		if (e == NULL)
			return NULL;
		res = parser_malloc(p, sizeof(*res));
		if (res == NULL)
			goto err_e;
		res->op = EO_NOT;
		res->v.sub[0] = e;
		return res;
	}

	case T_LEFT_PAREN: {
		if (lex(p) != 0)
			return NULL;
		e = parse_or(p);
		if (e == NULL)
			return NULL;
		if (p->token != T_RIGHT_PAREN) {
			*p->error = NULL;
			asprintf(p->error, "Right paren expected, got `%.*s'",
				 p->token_len, p->token_start);
			goto err_e;
		}
		if (lex(p) != 0)
			goto err_e;
		return e;
	}

	case T_FIELD_ESCAPE: case T_STRING:
		return parse_comparison(p);

	default:
		*p->error = NULL;
		asprintf(p->error, "Unexpected token `%.*s'", p->token_len,
			 p->token_start);
		return NULL;
	}
	abort();		/* Should never get here */

err_e:
	expr_free(e);
	return NULL;
}
Example #5
0
static int procValueRefArray(parseUnion * lvalp, ParserControl * parm)
{
   static XmlElement elm[] = {
      {NULL}
   };
   XmlAttr attr[1];
   if (tagEquals(parm->xmb, "VALUE.REFARRAY")) {
      if (attrsOk(parm->xmb, elm, attr, "VALUE.REFARRAY",
           ZTOK_VALUEARRAY)) {
         lvalp->xtokValueRefArray.max = 16;
         lvalp->xtokValueRefArray.next = 0;
         lvalp->xtokValueRefArray.values = (XtokValueReference*)parser_malloc(parm->heap, (sizeof(XtokValueReference) * lvalp->xtokValueRefArray.max));
         return XTOK_VALUEREFARRAY;
      }
   }
   return 0;
}
Example #6
0
/* Parse a comparison-expression string in *P.
   On success, return the parsed comparison-expression.
   On error, set *P->ERROR to an error string (for free()) or NULL, and return
   NULL. */
static struct expr *
parse_comparison(struct parsing *p)
{
	struct expr *res;

	res = parser_malloc(p, sizeof(*res));
	if (res == NULL)
		return NULL;
	if (p->token == T_FIELD_ESCAPE) {
		if (lex(p) != 0)
			goto err_res;
		if (p->token != T_STRING) {
			*p->error = strdup("Field name expected after field "
					   "escape");
			goto err_res;
		}
		if (strcmp(p->token_value, "regexp") == 0)
			return parse_comparison_regexp(p, res);
		res->virtual_field = 1;
		if (parse_escaped_field_name(&res->v.p.field.id, p->token_value)
		    != 0) {
			*p->error = NULL;
			asprintf(p->error, "Unknown escaped field name `%.*s'",
				 p->token_len, p->token_start);
			goto err_res;
		}
	} else {
		assert(p->token == T_STRING);
		res->virtual_field = 0;
		res->v.p.field.name = p->token_value;
		p->token_value = NULL;
	}
	if (lex(p) != 0)
		goto err_field;
	switch (p->token) {
	case EO_RAW_EQ: case EO_RAW_NE: case EO_INTERPRETED_EQ:
	case EO_INTERPRETED_NE:
		res->op = p->token;
		if (lex(p) != 0)
			goto err_field;
		if (p->token != T_STRING) {
			*p->error = NULL;
			asprintf(p->error, "Value expected, got `%.*s'",
				 p->token_len, p->token_start);
			goto err_field;
		}
		res->precomputed_value = 0;
		res->v.p.value.string = p->token_value;
		p->token_value = NULL;
		if (lex(p) != 0) {
			expr_free(res);
			return NULL;
		}
		break;

	case EO_VALUE_EQ: case EO_VALUE_NE: case EO_VALUE_LT: case EO_VALUE_LE:
	case EO_VALUE_GT: case EO_VALUE_GE:
		res->op = p->token;
		if (lex(p) != 0)
			goto err_field;
		if (p->token != T_STRING) {
			*p->error = NULL;
			asprintf(p->error, "Value expected, got `%.*s'",
				 p->token_len, p->token_start);
			goto err_field;
		}
		if (res->virtual_field == 0) {
			*p->error = NULL;
			asprintf (p->error, "Field `%s' does not support "
				  "value comparison",
				  res->v.p.field.name);
			goto err_field;
		} else {
			if (parse_virtual_field_value(res, p) != 0)
				goto err_field;
		}
		if (lex(p) != 0) {
			expr_free(res);
			return NULL;
		}
		break;

	default:
		*p->error = NULL;
		asprintf(p->error, "Operator expected, got `%.*s'",
			 p->token_len, p->token_start);
		goto err_field;
	}
	return res;

err_field:
	if (res->virtual_field == 0)
		free(res->v.p.field.name);
err_res:
	free(res);
	return NULL;
}
Example #7
0
/* Discard P->token_value, if any, and parse the next token in P->src.
   On success, return 0.
   On error, set *P->ERROR to an error string (for free()) or NULL, and return
   -1. */
static int
lex(struct parsing *p)
{
	free(p->token_value);
	p->token_value = NULL;
	while (*p->src == ' ' || *p->src == '\t' || *p->src == '\n')
		p->src++;
	p->token_start = p->src;
	switch (*p->src) {
	case '\0':
		p->token = T_EOF;
		break;

	case '!':
		p->src++;
		if (*p->src == '=' && p->src[1] == '=') {
			p->src += 2;
			p->token = EO_VALUE_NE;
			break;
		}
		p->token = EO_NOT;
		break;

	case '"': case '/': {
		char *buf, delimiter;
		size_t dest, buf_size;

		delimiter = *p->src;
		buf_size = 8;
		buf = parser_malloc(p, buf_size);
		if (buf == NULL)
			return -1;
		p->src++;
		dest = 0;
		while (*p->src != delimiter) {
			if (*p->src == '\0') {
				*p->error = strdup("Terminating delimiter "
						   "missing");
				free(buf);
				return -1;
			}
			if (*p->src == '\\') {
				p->src++;
				if (*p->src != '\\' && *p->src != delimiter) {
					*p->error = NULL;
					asprintf(p->error, "Unknown escape "
						 "sequence ``\\%c''", *p->src);
					free(buf);
					return -1;
				}
			}
			/* +1: make sure there is space for the terminating
			   NUL. */
			if (dest + 1 >= buf_size) {
				if (buf_size > SIZE_MAX / 2) {
					*p->error = strdup("Delimited string "
							   "too long");
					free(buf);
					return -1;
				}
				buf_size *= 2;
				buf = parser_realloc(p, buf, buf_size);
				if (buf == NULL) {
					*p->error = strdup("Out of memory");
					return -1;
				}
			}
			buf[dest] = *p->src;
			dest++;
			p->src++;
		}
		p->src++;
		buf[dest] = '\0';
		p->token_value = parser_realloc(p, buf, dest + 1);
		if (p->token_value == NULL)
			return -1;
		p->token = delimiter == '/' ? T_REGEXP : T_STRING;
		break;
	}

	case '&':
		p->src++;
		if (*p->src == '&') {
			p->src++;
			p->token = EO_AND;
			break;
		}
		p->token = T_UNKNOWN;
		break;

	case '(':
		p->src++;
		p->token = T_LEFT_PAREN;
		break;

	case ')':
		p->src++;
		p->token = T_RIGHT_PAREN;
		break;

	case '<':
		p->src++;
		if (*p->src == '=') {
			p->src++;
			p->token = EO_VALUE_LE;
			break;
		}
		p->token = EO_VALUE_LT;
		break;

	case '=':
		p->src++;
		if (*p->src == '=') {
			p->src++;
			p->token = EO_VALUE_EQ;
			break;
		}
		p->token = T_UNKNOWN;
		break;

	case '>':
		p->src++;
		if (*p->src == '=') {
			p->src++;
			p->token = EO_VALUE_GE;
			break;
		}
		p->token = EO_VALUE_GT;
		break;

	case '\\':
		p->src++;
		p->token = T_FIELD_ESCAPE;
		break;

	case '|':
		p->src++;
		if (*p->src == '|') {
			p->src++;
			p->token = EO_OR;
			break;
		}
		p->token = T_UNKNOWN;
		break;

	case 'i':
		if (p->src[1] == '=') {
			p->src += 2;
			p->token = EO_INTERPRETED_EQ;
			break;
		} else if (p->src[1] == '!' && p->src[2] == '=') {
			p->src += 3;
			p->token = EO_INTERPRETED_NE;
			break;
		}
		goto unquoted_string;

	case 'r':
		if (p->src[1] == '=') {
			p->src += 2;
			p->token = EO_RAW_EQ;
			break;
		} else if (p->src[1] == '!' && p->src[2] == '=') {
			p->src += 3;
			p->token = EO_RAW_NE;
			break;
		}
		goto unquoted_string;

	default:
		/* This assumes ASCII */
		assert ('Z' == 'A' + 25 && 'z' == 'a' + 25);
#define IS_UNQUOTED_STRING_CHAR(C)			\
			(((C) >= 'a' && (C) <= 'z')	\
			 || ((C) >= 'A' && (C) <= 'Z')	\
			 || ((C) >= '0' && (C) <= '9')	\
			 || (C) == '_')
		if (IS_UNQUOTED_STRING_CHAR(*p->src)) {
			size_t len;

		unquoted_string:
			do
				p->src++;
			while (IS_UNQUOTED_STRING_CHAR(*p->src));
			len = p->src - p->token_start;
			p->token_value = parser_malloc(p, len + 1);
			if (p->token_value == NULL)
				return -1;
			memcpy(p->token_value, p->token_start, len);
			p->token_value[len] = '\0';
			p->token = T_STRING;
			break;
		}
		p->src++;
		p->token = T_UNKNOWN;
		break;
	}
	if (p->src - p->token_start > INT_MAX) {
		*p->error = strdup("Token too long");
		return -1;
	}
	p->token_len = p->src - p->token_start;
	return 0;
}
Example #8
0
/**
 * Processes the start of element events while parsing.
 * 
 * @param userData the parsing context
 * @param name the element name
 * @param atts the element attributes
 */
static void CP_XMLCALL start_element_handler(
	void *userData, const XML_Char *name, const XML_Char **atts) {
	static const XML_Char * const req_plugin_atts[] = { "id", NULL };
	static const XML_Char * const opt_plugin_atts[] = { "name", "version", "provider-name", NULL };
	static const XML_Char * const req_bwcompatibility_atts[] = { NULL };
	static const XML_Char * const opt_bwcompatibility_atts[] = { "abi", "api", NULL };
	static const XML_Char * const req_cpluff_atts[] = { "version", NULL };
	static const XML_Char * const opt_cpluff_atts[] = { NULL };
	static const XML_Char * const req_import_atts[] = { "plugin", NULL };
	static const XML_Char * const opt_import_atts[] = { "version", "optional", NULL };
	static const XML_Char * const req_runtime_atts[] = { "library", NULL };
	static const XML_Char * const opt_runtime_atts[] = { "funcs", NULL };
	static const XML_Char * const req_ext_point_atts[] = { "id", NULL };
	static const XML_Char * const opt_ext_point_atts[] = { "name", "schema", NULL };
	static const XML_Char * const req_extension_atts[] = { "point", NULL };
	//static const XML_Char * const opt_extension_atts[] = { "id", "name", NULL };
	ploader_context_t *plcontext = userData;
	unsigned int i;

	// Process element start 
	switch (plcontext->state) {

		case PARSER_BEGIN:
			if (!strcmp(name, "plugin")) {
				plcontext->state = PARSER_PLUGIN;
				if (!check_attributes(plcontext, name, atts,
						req_plugin_atts, opt_plugin_atts)) {
					break;
				}
				for (i = 0; atts[i] != NULL; i += 2) {
					if (!strcmp(atts[i], "name")) {
						plcontext->plugin->name
							= parser_strdup(plcontext, atts[i+1]);
					} else if (!strcmp(atts[i], "id")) {
						plcontext->plugin->identifier
							= parser_strdup(plcontext, atts[i+1]);
					} else if (!strcmp(atts[i], "version")) {
						plcontext->plugin->version
							= parser_strdup(plcontext, atts[i+1]);
					} else if (!strcmp(atts[i], "provider-name")) {
						plcontext->plugin->provider_name
							= parser_strdup(plcontext, atts[i+1]);
					} else if(!strcmp(atts[i],"url")){
						plcontext->plugin->url = parser_strdup(plcontext, atts[i+1]);
					} else if(!strcmp(atts[i],"resourcetype")){
						plcontext->plugin->resourcetype = parser_strdup(plcontext, atts[i+1]);
					}
				}
			} else {
				unexpected_element(plcontext, name);
			}
			break;

		case PARSER_PLUGIN:
			if (!strcmp(name, "backwards-compatibility")) {
				if (check_attributes(plcontext, name, atts,
						req_bwcompatibility_atts, opt_bwcompatibility_atts)) {
					for (i = 0; atts[i] != NULL; i += 2) {
						if (!strcmp(atts[i], "abi")) {
							plcontext->plugin->abi_bw_compatibility = parser_strdup(plcontext, atts[i+1]);
						} else if (!strcmp(atts[i], "api")) {
							plcontext->plugin->api_bw_compatibility = parser_strdup(plcontext, atts[i+1]);
						}
					}
				}
			} else if (!strcmp(name, "requires")) {
				plcontext->state = PARSER_REQUIRES;
			} else if (!strcmp(name, "runtime")) {
				if (check_attributes(plcontext, name, atts,
						req_runtime_atts, opt_runtime_atts)) {
					for (i = 0; atts[i] != NULL; i += 2) {
						if (!strcmp(atts[i], "library")) {
							plcontext->plugin->runtime_lib_name
								= parser_strdup(plcontext, atts[i+1]);
						} else if (!strcmp(atts[i], "funcs")) {
							plcontext->plugin->runtime_funcs_symbol
								= parser_strdup(plcontext, atts[i+1]);
						}
					}
				}
			} else if (!strcmp(name, "extension-point")) {
				if (check_attributes(plcontext, name, atts,
						req_ext_point_atts, opt_ext_point_atts)) {
					cp_ext_point_t *ext_point;
					
					// Allocate space for extension points, if necessary 
					if (plcontext->plugin->num_ext_points == plcontext->ext_points_size) {
						cp_ext_point_t *nep;
						size_t ns;
						
						if (plcontext->ext_points_size == 0) {
							ns = 4;
						} else {
							ns = plcontext->ext_points_size * 2;
						}
						if ((nep = realloc(plcontext->plugin->ext_points,
								ns * sizeof(cp_ext_point_t))) == NULL) {
							resource_error(plcontext);
							break;
						}
						plcontext->plugin->ext_points = nep;
						plcontext->ext_points_size = ns;
					}
					
					// Parse extension point specification 
					ext_point = plcontext->plugin->ext_points
						+ plcontext->plugin->num_ext_points;
					memset(ext_point, 0, sizeof(cp_ext_point_t));
					ext_point->plugin = plcontext->plugin;
					ext_point->name = NULL;
					ext_point->local_id = NULL;
					ext_point->identifier = NULL;
					ext_point->schema_path = NULL;
					for (i = 0; atts[i] != NULL; i += 2) {
						if (!strcmp(atts[i], "name")) {
							ext_point->name
								= parser_strdup(plcontext, atts[i+1]);
						} else if (!strcmp(atts[i], "id")) {
							ext_point->local_id
								= parser_strdup(plcontext, atts[i+1]);
							ext_point->identifier
								= parser_strscat(plcontext,
									plcontext->plugin->identifier, ".", atts[i+1], NULL);
						} else if (!strcmp(atts[i], "schema")) {
							ext_point->schema_path
								= parser_strdup(plcontext, atts[i+1]);
						}
					}
					plcontext->plugin->num_ext_points++;
					
				}
			} else if (!(strcmp(name, "extension"))) {
				plcontext->state = PARSER_EXTENSION;
				plcontext->depth = 0;
				if (check_req_attributes(
					plcontext, name, atts, req_extension_atts)) {
					cp_extension_t *extension;
				
					// Allocate space for extensions, if necessary 
					if (plcontext->plugin->num_extensions == plcontext->extensions_size) {
						cp_extension_t *ne;
						size_t ns;
						
						if (plcontext->extensions_size == 0) {
							ns = 16;
						} else {
							ns = plcontext->extensions_size * 2;
						}
						if ((ne = realloc(plcontext->plugin->extensions,
								ns * sizeof(cp_extension_t))) == NULL) {
							resource_error(plcontext);
							break;
						}
						plcontext->plugin->extensions = ne;
						plcontext->extensions_size = ns;
					}
					
					// Parse extension attributes 
					extension = plcontext->plugin->extensions
						+ plcontext->plugin->num_extensions;
					memset(extension, 0, sizeof(cp_extension_t));
					extension->plugin = plcontext->plugin;
					extension->name = NULL;
					extension->local_id = NULL;
					extension->identifier = NULL;
					extension->ext_point_id = NULL;
					extension->configuration = NULL;
					for (i = 0; atts[i] != NULL; i += 2) {
						if (!strcmp(atts[i], "point")) {
							extension->ext_point_id
								= parser_strdup(plcontext, atts[i+1]);
						} else if (!strcmp(atts[i], "id")) {
							extension->local_id
								= parser_strdup(plcontext, atts[i+1]);
							extension->identifier
								= parser_strscat(plcontext,
									plcontext->plugin->identifier, ".", atts[i+1], NULL);
						} else if (!strcmp(atts[i], "name")) {
							extension->name
								= parser_strdup(plcontext, atts[i+1]);
						}
					}
					plcontext->plugin->num_extensions++;
					
					// Initialize configuration parsing 
					if ((extension->configuration = plcontext->configuration
						= parser_malloc(plcontext, sizeof(cp_cfg_element_t))) != NULL) {
						init_cfg_element(plcontext, plcontext->configuration, name, atts, NULL);
					}
					XML_SetCharacterDataHandler(plcontext->parser, character_data_handler);
				}
			} else {
				unexpected_element(plcontext, name);
			}
			break;

		case PARSER_REQUIRES:
			if (!strcmp(name, "c-pluff")) {
				if (check_attributes(plcontext, name, atts,
						req_cpluff_atts, opt_cpluff_atts)) {
					for (i = 0; atts[i] != NULL; i += 2) {
						if (!strcmp(atts[i], "version")) {
							plcontext->plugin->req_cpluff_version = parser_strdup(plcontext, atts[i+1]);
						}
					}
				}
			} else if (!strcmp(name, "import")) {
				if (check_attributes(plcontext, name, atts,
						req_import_atts, opt_import_atts)) {
					cp_plugin_import_t *import = NULL;
				
					// Allocate space for imports, if necessary 
					if (plcontext->plugin->num_imports == plcontext->imports_size) {
						cp_plugin_import_t *ni;
						size_t ns;
					
						if (plcontext->imports_size == 0) {
							ns = 16;
						} else {
							ns = plcontext->imports_size * 2;
						}
						if ((ni = realloc(plcontext->plugin->imports,
								ns * sizeof(cp_plugin_import_t))) == NULL) {
							resource_error(plcontext);
							break;
						}
						plcontext->plugin->imports = ni;
						plcontext->imports_size = ns;
					}
				
					// Parse import specification 
					import = plcontext->plugin->imports
						+ plcontext->plugin->num_imports;
					memset(import, 0, sizeof(cp_plugin_import_t));
					import->plugin_id = NULL;
					import->version = NULL;
					for (i = 0; atts[i] != NULL; i += 2) {
						if (!strcmp(atts[i], "plugin")) {
							import->plugin_id
								= parser_strdup(plcontext, atts[i+1]);
						} else if (!strcmp(atts[i], "version")) {
							import->version = parser_strdup(plcontext, atts[i+1]);
						} else if (!strcmp(atts[i], "optional")) {
							if (!strcmp(atts[i+1], "true")
								|| !strcmp(atts[i+1], "1")) {
								import->optional = 1;
							} else if (strcmp(atts[i+1], "false")
								&& strcmp(atts[i+1], "0")) {
								descriptor_errorf(plcontext, 0, _("unknown boolean value: %s"), atts[i+1]);
							}
						}
					}
					plcontext->plugin->num_imports++;
				}
			} else {
				unexpected_element(plcontext, name);
			}
			break;

		case PARSER_EXTENSION:
			plcontext->depth++;
			if (plcontext->configuration != NULL && plcontext->skippedCEs == 0) {
				cp_cfg_element_t *ce;
				
				// Allocate more space for children, if necessary 
				if (plcontext->configuration->num_children == plcontext->configuration->index) {
					cp_cfg_element_t *nce;
					size_t ns;
						
					if (plcontext->configuration->index == 0) {
						ns = 16;
					} else {
						ns = plcontext->configuration->index * 2;
					}
					if ((nce = realloc(plcontext->configuration->children,
							ns * sizeof(cp_cfg_element_t))) == NULL) {
						plcontext->skippedCEs++;
						resource_error(plcontext);
						break;
					}
					plcontext->configuration->children = nce;
					plcontext->configuration->index = ns;
				}
				
				// Save possible value 
				if (plcontext->value != NULL) {
					plcontext->value[plcontext->value_length] = '\0';
					plcontext->configuration->value = plcontext->value;
				}
				
				ce = plcontext->configuration->children + plcontext->configuration->num_children;
				init_cfg_element(plcontext, ce, name, atts, plcontext->configuration);
				plcontext->configuration->num_children++;
				plcontext->configuration = ce;
			}
			break;
			
		case PARSER_UNKNOWN:
			plcontext->depth++;
			break;
		default:
			unexpected_element(plcontext, name);
			break;
	}
}