netflow: Back-out optimization that could lead to infinite loop
[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 bool
45 ovsdb_parser_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 && value->type == JSON_STRING
86             && ovsdb_parser_is_id(value->u.string)))
87     {
88         svec_add(&parser->used, name);
89         return value;
90     } else {
91         ovsdb_parser_raise_error(parser, "Type mismatch for member '%s'.",
92                                  name);
93         return NULL;
94     }
95 }
96
97 void
98 ovsdb_parser_raise_error(struct ovsdb_parser *parser, const char *format, ...)
99 {
100     if (!parser->error) {
101         struct ovsdb_error *error;
102         va_list args;
103         char *message;
104
105         va_start(args, format);
106         message = xvasprintf(format, args);
107         va_end(args);
108
109         error = ovsdb_syntax_error(parser->json, NULL, "Parsing %s failed: %s",
110                                    parser->name, message);
111         free(message);
112
113         parser->error = error;
114     }
115 }
116
117 struct ovsdb_error *
118 ovsdb_parser_get_error(const struct ovsdb_parser *parser)
119 {
120     return parser->error ? ovsdb_error_clone(parser->error) : NULL;
121 }
122
123 bool
124 ovsdb_parser_has_error(const struct ovsdb_parser *parser)
125 {
126     return parser->error != NULL;
127 }
128
129 struct ovsdb_error *
130 ovsdb_parser_finish(struct ovsdb_parser *parser)
131 {
132     if (!parser->error) {
133         const struct shash *object = json_object(parser->json);
134         size_t n_unused;
135
136         /* XXX this method of detecting unused members can be made cheaper */
137         svec_sort_unique(&parser->used);
138         n_unused = shash_count(object) - parser->used.n;
139         if (n_unused) {
140             struct shash_node *node;
141
142             SHASH_FOR_EACH (node, object) {
143                 if (!svec_contains(&parser->used, node->name)) {
144                     if (n_unused > 1) {
145                         ovsdb_parser_raise_error(
146                             parser,
147                             "Member '%s' and %zu other member%s "
148                             "are present but not allowed here.",
149                             node->name, n_unused - 1, n_unused > 2 ? "s" : "");
150                     } else {
151                         ovsdb_parser_raise_error(
152                             parser,
153                             "Member '%s' is present but not allowed here.",
154                             node->name);
155                     }
156                     break;
157                 }
158             }
159         }
160     }
161
162     free(parser->name);
163     svec_destroy(&parser->used);
164
165     return parser->error;
166 }