aboutsummaryrefslogtreecommitdiff
path: root/src/XpathParser.c
blob: b69e47d20a54fd0e67f95106c6cc18604e9566b1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include "../include/quickmedia/XpathParser.h"
#include "../include/quickmedia/XpathTokenizer.h"
#include <stdlib.h>

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