#include "../include/quickmedia/XpathParser.h" #include "../include/quickmedia/XpathTokenizer.h" #include typedef struct { QuickMediaXpathTokenizer tokenizer; } QuickMediaXpathParser; static int contains_glob_char(QuickMediaStringView str) { for(size_t i = 0; i < str.size; ++i) { if(str.data[i] == '*') return 1; } return 0; } static void quickmedia_xpath_parser_init(QuickMediaXpathParser *self, const char *xpath) { quickmedia_xpath_tokenizer_init(&self->tokenizer, xpath); } /* (('[' IDENTIFIER '=' '"' STRING '"' ']') | ('[' NUMBER ']'))? */ static int xpath_parse_param(QuickMediaXpathParser *self, QuickMediaNodeSearchParam *result) { if(quickmedia_xpath_tokenizer_next_if(&self->tokenizer, QUICKMEDIA_XPATH_TOKEN_OPEN_BRACKET) != 0) return 1; QuickMediaXpathToken token = quickmedia_xpath_tokenizer_next(&self->tokenizer); if(token == QUICKMEDIA_XPATH_TOKEN_IDENTIFIER) { result->name = self->tokenizer.identifier; token = quickmedia_xpath_tokenizer_next(&self->tokenizer); if(token != QUICKMEDIA_XPATH_TOKEN_EQUAL) return -2; token = quickmedia_xpath_tokenizer_next(&self->tokenizer); if(token != QUICKMEDIA_XPATH_TOKEN_STRING) return -3; result->value = self->tokenizer.string; token = quickmedia_xpath_tokenizer_next(&self->tokenizer); if(token != QUICKMEDIA_XPATH_TOKEN_CLOSING_BRACKET) return -4; result->defined = 1; result->value_is_glob = contains_glob_char(result->value); return 0; } else if(token == QUICKMEDIA_XPATH_TOKEN_NUMBER) { result->index = self->tokenizer.number; token = quickmedia_xpath_tokenizer_next(&self->tokenizer); if(token != QUICKMEDIA_XPATH_TOKEN_CLOSING_BRACKET) return -4; return 0; } else { return -1; } } /* ('[' NUMBER ']'))? */ static int xpath_parse_index(QuickMediaXpathParser *self, QuickMediaNodeSearchParam *result) { if(quickmedia_xpath_tokenizer_next_if(&self->tokenizer, QUICKMEDIA_XPATH_TOKEN_OPEN_BRACKET) != 0) return 1; QuickMediaXpathToken token = quickmedia_xpath_tokenizer_next(&self->tokenizer); if(token != QUICKMEDIA_XPATH_TOKEN_NUMBER) return -1; result->index = self->tokenizer.number; token = quickmedia_xpath_tokenizer_next(&self->tokenizer); if(token != QUICKMEDIA_XPATH_TOKEN_CLOSING_BRACKET) return -4; return 0; } static int xpath_parse_node(QuickMediaXpathParser *self, QuickMediaNodeSearch *result) { quickmedia_node_search_init(result); QuickMediaXpathToken token = quickmedia_xpath_tokenizer_next(&self->tokenizer); /* // or / */ if(token == QUICKMEDIA_XPATH_TOKEN_CHILD || token == QUICKMEDIA_XPATH_TOKEN_CHILD_RECURSIVE) { result->recursive = (token == QUICKMEDIA_XPATH_TOKEN_CHILD_RECURSIVE); token = quickmedia_xpath_tokenizer_next(&self->tokenizer); if(token != QUICKMEDIA_XPATH_TOKEN_IDENTIFIER) return -1; result->name = self->tokenizer.identifier; int param_result = xpath_parse_param(self, &result->param); if(param_result < 0) return param_result; int index_result = xpath_parse_index(self, &result->param); if(index_result < 0) return index_result; result->child = malloc(sizeof(QuickMediaNodeSearch)); if(!result->child) return -1; int node_result = xpath_parse_node(self, result->child); if(node_result > 0) { node_result = 0; /* Didn't have child, remove child */ free(result->child); result->child = NULL; } return node_result; } else if(token == QUICKMEDIA_XPATH_TOKEN_END_OF_FILE) { return 1; } else { return -2; } } int quickmedia_parse_xpath(const char *xpath, QuickMediaNodeSearch *result) { QuickMediaXpathParser parser; quickmedia_xpath_parser_init(&parser, xpath); int parse_result = xpath_parse_node(&parser, result); if(parse_result > 0) parse_result = -1; return parse_result; }