/* 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; }
/** * 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; }
/* 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; }
/* 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; }
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; }
/* 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; }
/* 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; }
/** * 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; } }