xref: /netbsd-src/external/mpl/dhcp/dist/keama/json.c (revision f407d9293b6650aa8c33d6a995f797bb6aaefd90)
1 /*	$NetBSD: json.c,v 1.3 2022/04/03 01:10:59 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2017-2022 Internet Systems Consortium, Inc. ("ISC")
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
16  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  *   Internet Systems Consortium, Inc.
19  *   PO Box 360
20  *   Newmarket, NH 03857 USA
21  *   <info@isc.org>
22  *   https://www.isc.org/
23  *
24  */
25 
26 #include <sys/cdefs.h>
27 __RCSID("$NetBSD: json.c,v 1.3 2022/04/03 01:10:59 christos Exp $");
28 
29 /* From Kea src/lib/cc/data.cc fromJSON() */
30 
31 #include "keama.h"
32 
33 #include <stdlib.h>
34 #include <string.h>
35 
36 struct element *
json_parse(struct parse * cfile)37 json_parse(struct parse *cfile)
38 {
39 	struct element *elem;
40 	const char *val;
41 	enum dhcp_token token;
42 
43 	elem = create();
44 	stackPush(cfile, elem);
45 	cfile->stack[0] = elem;
46 	cfile->stack_top = 0;
47 
48 	token = next_token(&val, NULL, cfile);
49 	switch (token) {
50 	case NUMBER:
51 		elem = createInt(atoll(val));
52 		TAILQ_CONCAT(&elem->comments, &cfile->comments);
53 		break;
54 	case STRING:
55 		elem = createString(makeString(-1, val));
56 		TAILQ_CONCAT(&elem->comments, &cfile->comments);
57 		break;
58 	case NAME:
59 		if (strcmp(val, "null") == 0)
60 			elem = createNull();
61 		else if (strcmp(val, "true") == 0)
62 			elem = createBool(ISC_TRUE);
63 		else if (strcmp(val, "false") == 0) {
64 			elem = createBool(ISC_FALSE);
65 			elem->skip = ISC_TRUE;
66 		} else
67 			parse_error(cfile, "unknown name %s", val);
68 		TAILQ_CONCAT(&elem->comments, &cfile->comments);
69 		break;
70 	case LBRACKET:
71 		elem = json_list_parse(cfile);
72 		break;
73 	case LBRACE:
74 		elem = json_map_parse(cfile);
75 		break;
76 	case END_OF_FILE:
77 		parse_error(cfile, "unexpected end of file");
78 	default:
79 		parse_error(cfile, "unexpected %s", val);
80 	}
81 	return elem;
82 }
83 
84 struct element *
json_list_parse(struct parse * cfile)85 json_list_parse(struct parse *cfile)
86 {
87 	struct element *list;
88 	struct element *item;
89 	const char *val;
90 	enum dhcp_token token;
91 	isc_boolean_t done = ISC_FALSE;
92 
93 	list = createList();
94 	TAILQ_CONCAT(&list->comments, &cfile->comments);
95 	stackPush(cfile, list);
96 	do {
97 		token = peek_token(&val, NULL, cfile);
98 		switch (token) {
99 		case RBRACKET:
100 			done = ISC_TRUE;
101 			break;
102 		case END_OF_FILE:
103 			parse_error(cfile, "unexpected end of file");
104 		case COMMA:
105 			skip_token(&val, NULL, cfile);
106 			if (listSize(list) == 0)
107 				parse_error(cfile, "unexpected ','");
108 			item = json_parse(cfile);
109 			listPush(list, item);
110 			break;
111 		default:
112 			if (listSize(list) > 0)
113 				parse_error(cfile, "expected ','");
114 			item = json_parse(cfile);
115 			listPush(list, item);
116 			break;
117 		}
118 	} while (!done);
119 	skip_token(&val, NULL, cfile);
120 	cfile->stack_top--;
121 	return list;
122 }
123 
124 struct element *
json_map_parse(struct parse * cfile)125 json_map_parse(struct parse *cfile)
126 {
127 	struct element *map;
128 	struct element *item;
129 	const char *val;
130 	const char *key;
131 	enum dhcp_token token;
132 	isc_boolean_t done = ISC_FALSE;
133 
134 	map = createMap();
135 	TAILQ_CONCAT(&map->comments, &cfile->comments);
136 	stackPush(cfile, map);
137 	do {
138 		token = peek_token(&val, NULL, cfile);
139 		switch (token) {
140 		case RBRACE:
141 			done = ISC_TRUE;
142 			break;
143 		case END_OF_FILE:
144 			parse_error(cfile, "unexpected end of file");
145 		case COMMA:
146 			skip_token(&val, NULL, cfile);
147 			if (mapSize(map) == 0)
148 				parse_error(cfile, "unexpected ','");
149 			token = next_token(&val, NULL, cfile);
150 			if (token != STRING)
151 				parse_error(cfile, "unexpected %s, "
152 					    "expected \"key\":value", val);
153 			key = strdup(val);
154 			token = next_token(&val, NULL, cfile);
155 			if (token != COLON)
156 				parse_error(cfile, "unexpected %s, "
157 					    "expected ':'", val);
158 			item = json_parse(cfile);
159 			mapSet(map, item, key);
160 			break;
161 		case STRING:
162 			skip_token(&val, NULL, cfile);
163 			if (mapSize(map) > 0)
164 				parse_error(cfile, "unexpected \"%s\", "
165 					    "expected ','", val);
166 			key = strdup(val);
167 			token = next_token(&val, NULL, cfile);
168 			if (token != COLON)
169 				parse_error(cfile, "unexpected %s, "
170 					    "expected ':'", val);
171 			item = json_parse(cfile);
172 			mapSet(map, item, key);
173 			break;
174 		default:
175 			if (mapSize(map) == 0)
176 				parse_error(cfile, "unexpected %s, "
177 					    "expected \"key\":value or '}'",
178 					    val);
179 			else
180 				parse_error(cfile, "unexpected %s, "
181 					    "expected ',' or '}'", val);
182 		}
183 	} while (!done);
184 	skip_token(&val, NULL, cfile);
185 	cfile->stack_top--;
186 	return map;
187 }
188