Initial implementation of OVSDB.
[sliver-openvswitch.git] / lib / ovsdb-parser.c
1 /* Copyright (c) 2009 Nicira Networks
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <config.h>
17
18 #include "ovsdb-parser.h"
19
20 #include <ctype.h>
21 #include <stdarg.h>
22
23 #include "ovsdb-error.h"
24
25 void
26 ovsdb_parser_init(struct ovsdb_parser *parser, const struct json *json,
27                   const char *name, ...)
28 {
29     va_list args;
30
31     va_start(args, name);
32     parser->name = xvasprintf(name, args);
33     va_end(args);
34
35     svec_init(&parser->used);
36     parser->error = NULL;
37
38     parser->json = (json && json->type == JSON_OBJECT ? json : NULL);
39     if (!parser->json) {
40         ovsdb_parser_raise_error(parser, "Object expected.");
41     }
42 }
43
44 static bool
45 is_id(const char *string)
46 {
47     unsigned char c;
48
49     c = *string;
50     if (!isalpha(c) && c != '_') {
51         return false;
52     }
53
54     for (;;) {
55         c = *++string;
56         if (c == '\0') {
57             return true;
58         } else if (!isalpha(c) && !isdigit(c) && c != '_') {
59             return false;
60         }
61     }
62 }
63
64 const struct json *
65 ovsdb_parser_member(struct ovsdb_parser *parser, const char *name,
66                     enum ovsdb_parser_types types)
67 {
68     struct json *value;
69
70     if (!parser->json) {
71         return NULL;
72     }
73
74     value = shash_find_data(json_object(parser->json), name);
75     if (!value) {
76         if (!(types & OP_OPTIONAL)) {
77             ovsdb_parser_raise_error(parser,
78                                      "Required '%s' member is missing.", name);
79         }
80         return NULL;
81     }
82
83     if (value->type >= 0 && value->type < JSON_N_TYPES
84         && (types & (1u << value->type)
85             || (types & OP_ID
86                 && value->type == JSON_STRING
87                 && is_id(value->u.string))))
88     {
89         svec_add(&parser->used, name);
90         return value;
91     } else {
92         ovsdb_parser_raise_error(parser, "Type mismatch for member '%s'.",
93                                  name);
94         return NULL;
95     }
96 }
97
98 void
99 ovsdb_parser_raise_error(struct ovsdb_parser *parser, const char *format, ...)
100 {
101     if (!parser->error) {
102         struct ovsdb_error *error;
103         va_list args;
104         char *message;
105
106         va_start(args, format);
107         message = xvasprintf(format, args);
108         va_end(args);
109
110         error = ovsdb_syntax_error(parser->json, NULL, "Parsing %s failed: %s",
111                                    parser->name, message);
112         free(message);
113
114         parser->error = error;
115     }
116 }
117
118 struct ovsdb_error *
119 ovsdb_parser_get_error(const struct ovsdb_parser *parser)
120 {
121     return parser->error ? ovsdb_error_clone(parser->error) : NULL;
122 }
123
124 bool
125 ovsdb_parser_has_error(const struct ovsdb_parser *parser)
126 {
127     return parser->error != NULL;
128 }
129
130 struct ovsdb_error *
131 ovsdb_parser_finish(struct ovsdb_parser *parser)
132 {
133     if (!parser->error) {
134         const struct shash *object = json_object(parser->json);
135         size_t n_unused;
136
137         /* XXX this method of detecting unused members can be made cheaper */
138         svec_sort_unique(&parser->used);
139         n_unused = shash_count(object) - parser->used.n;
140         if (n_unused) {
141             struct shash_node *node;
142
143             SHASH_FOR_EACH (node, object) {
144                 if (!svec_contains(&parser->used, node->name)) {
145                     if (n_unused > 1) {
146                         ovsdb_parser_raise_error(
147                             parser,
148                             "Member '%s' and %zu other member%s "
149                             "are present but not allowed here.",
150                             node->name, n_unused - 1, n_unused > 2 ? "s" : "");
151                     } else {
152                         ovsdb_parser_raise_error(
153                             parser,
154                             "Member '%s' is present but not allowed here.",
155                             node->name);
156                     }
157                     break;
158                 }
159             }
160         }
161     }
162
163     free(parser->name);
164     svec_destroy(&parser->used);
165
166     return parser->error;
167 }