1 /*
2 *
3 ***** BEGIN LICENSE BLOCK *****
4
5 Copyright (C) 2009-2020 Olof Hagsand
6
7 This file is part of CLIXON.
8
9 Licensed under the Apache License, Version 2.0 (the "License");
10 you may not use this file except in compliance with the License.
11 You may obtain a copy of the License at
12
13 http://www.apache.org/licenses/LICENSE-2.0
14
15 Unless required by applicable law or agreed to in writing, software
16 distributed under the License is distributed on an "AS IS" BASIS,
17 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 See the License for the specific language governing permissions and
19 limitations under the License.
20
21 Alternatively, the contents of this file may be used under the terms of
22 the GNU General Public License Version 3 or later (the "GPL"),
23 in which case the provisions of the GPL are applicable instead
24 of those above. If you wish to allow use of your version of this file only
25 under the terms of the GPL, and not to allow others to
26 use your version of this file under the terms of Apache License version 2, indicate
27 your decision by deleting the provisions above and replace them with the
28 notice and other provisions required by the GPL. If you do not delete
29 the provisions above, a recipient may use your version of this file under
30 the terms of any one of the Apache License version 2 or the GPL.
31
32 ***** END LICENSE BLOCK *****
33
34 * Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10
35 * This file defines XPATH contexts used in traversing the XPATH parse tree.
36 */
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <errno.h>
41 #include <string.h>
42 #include <limits.h>
43 #include <stdint.h>
44 #include <syslog.h>
45 #include <fcntl.h>
46 #include <math.h> /* NaN */
47
48 /* cligen */
49 #include <cligen/cligen.h>
50
51 /* clicon */
52 #include "clixon_err.h"
53 #include "clixon_log.h"
54 #include "clixon_string.h"
55 #include "clixon_queue.h"
56 #include "clixon_hash.h"
57 #include "clixon_handle.h"
58 #include "clixon_yang.h"
59 #include "clixon_xml.h"
60 #include "clixon_xpath_ctx.h"
61 #include "clixon_xpath.h"
62 #include "clixon_xpath_parse.h"
63
64 /*
65 * Variables
66 */
67 const map_str2int ctxmap[] = {
68 {"nodeset", XT_NODESET},
69 {"bool", XT_BOOL},
70 {"number", XT_NUMBER},
71 {"string", XT_STRING},
72 {NULL, -1}
73 };
74
75 /*! Free xpath context */
76 int
ctx_free(xp_ctx * xc)77 ctx_free(xp_ctx *xc)
78 {
79 if (xc->xc_nodeset)
80 free(xc->xc_nodeset);
81 if (xc->xc_string)
82 free(xc->xc_string);
83 free(xc);
84 return 0;
85 }
86
87 /*! Duplicate xpath context */
88 xp_ctx *
ctx_dup(xp_ctx * xc0)89 ctx_dup(xp_ctx *xc0)
90 {
91 static xp_ctx *xc = NULL;
92
93 if ((xc = malloc(sizeof(*xc))) == NULL){
94 clicon_err(OE_UNIX, errno, "malloc");
95 goto done;
96 }
97 memset(xc, 0, sizeof(*xc));
98 *xc = *xc0;
99 if (xc0->xc_size){
100 if ((xc->xc_nodeset = calloc(xc0->xc_size, sizeof(cxobj*))) == NULL){
101 clicon_err(OE_UNIX, errno, "calloc");
102 goto done;
103 }
104 memcpy(xc->xc_nodeset, xc0->xc_nodeset, xc->xc_size*sizeof(cxobj*));
105 }
106 if (xc0->xc_string)
107 if ((xc->xc_string = strdup(xc0->xc_string)) == NULL){
108 clicon_err(OE_UNIX, errno, "strdup");
109 goto done;
110 }
111 done:
112 return xc;
113 }
114
115 /*! Print XPATH context to CLIgen buf
116 * @param[in] cb CLIgen buf to print to
117 * @param[in] xc XPATH evaluation context
118 * @param[in] ind Indentation margin
119 * @param[in] str Prefix string in printout
120 */
121 int
ctx_print_cb(cbuf * cb,xp_ctx * xc,int ind,char * str)122 ctx_print_cb(cbuf *cb,
123 xp_ctx *xc,
124 int ind,
125 char *str)
126 {
127 static int indent = 0;
128 int i;
129
130 if (ind<0)
131 indent += ind;
132 cprintf(cb, "%*s%s ", indent, "", str?str:"");
133 if (ind>0)
134 indent += ind;
135 if (xc){
136 cprintf(cb, "%s: ", (char*)clicon_int2str(ctxmap, xc->xc_type));
137 switch (xc->xc_type){
138 case XT_NODESET:
139 for (i=0; i<xc->xc_size; i++)
140 cprintf(cb, "%s ", xml_name(xc->xc_nodeset[i]));
141 break;
142 case XT_BOOL:
143 cprintf(cb, "%s", xc->xc_bool?"true":"false");
144 break;
145 case XT_NUMBER:
146 cprintf(cb, "%lf", xc->xc_number);
147 break;
148 case XT_STRING:
149 cprintf(cb, "%s", xc->xc_string);
150 break;
151 }
152 }
153 return 0;
154 }
155
156 /*! Print XPATH context
157 * @param[in] f File to print to
158 * @param[in] xc XPATH evaluation context
159 * @param[in] str Prefix string in printout
160 */
161 int
ctx_print(FILE * f,xp_ctx * xc,char * str)162 ctx_print(FILE *f,
163 xp_ctx *xc,
164 char *str)
165 {
166 int retval = -1;
167 cbuf *cb = NULL;
168
169 if ((cb = cbuf_new()) == NULL){
170 clicon_err(OE_UNIX, errno, "cbuf_new");
171 goto done;
172 }
173 ctx_print_cb(cb, xc, 0, str);
174 fprintf(f, "%s", cbuf_get(cb));
175 retval = 0;
176 done:
177 if (cb)
178 cbuf_free(cb);
179 return retval;
180 }
181
182 /*! Convert xpath context to boolean according to boolean() function in XPATH spec
183 * @param[in] xc XPATH context
184 * @retval 0 False
185 * @retval 1 True
186 * a number is true if and only if it is neither positive or negative zero nor NaN
187 * a node-set is true if and only if it is non-empty
188 * a string is true if and only if its length is non-zero
189 * an object of a type other than the four basic types is converted to a boolean
190 * in a way that is dependent on that type
191 */
192 int
ctx2boolean(xp_ctx * xc)193 ctx2boolean(xp_ctx *xc)
194 {
195 int b = -1;
196 switch (xc->xc_type){
197 case XT_NODESET:
198 b = (xc->xc_size != 0);
199 break;
200 case XT_BOOL:
201 b = xc->xc_bool;
202 break;
203 case XT_NUMBER:
204 b = (xc->xc_number != 0.0 && xc->xc_number != NAN);
205 break;
206 case XT_STRING:
207 b = (xc->xc_string && strlen(xc->xc_string));
208 break;
209 }
210 return b;
211 }
212
213 /*! Convert xpath context to string according to string() function in XPATH spec
214 * @param[in] xc XPATH context
215 * @param[out] str0 Malloced result string
216 * @retval 0 OK
217 * @retval -1 Error
218 * @note string malloced.
219 */
220 int
ctx2string(xp_ctx * xc,char ** str0)221 ctx2string(xp_ctx *xc,
222 char **str0)
223 {
224 int retval = -1;
225 char *str = NULL;
226 int len;
227 char *b;
228
229 switch (xc->xc_type){
230 case XT_NODESET:
231 if (xc->xc_size && (b = xml_body(xc->xc_nodeset[0]))){
232 if ((str = strdup(b)) == NULL){
233 clicon_err(OE_XML, errno, "strdup");
234 goto done;
235 }
236 }
237 else
238 if ((str = strdup("")) == NULL){
239 clicon_err(OE_XML, errno, "strdup");
240 goto done;
241 }
242 break;
243 case XT_BOOL:
244 if ((str = strdup(xc->xc_bool == 0?"false":"true")) == NULL){
245 clicon_err(OE_XML, errno, "strdup");
246 goto done;
247 }
248 break;
249 case XT_NUMBER:
250 len = snprintf(NULL, 0, "%0lf", xc->xc_number);
251 len++;
252 if ((str = malloc(len)) == NULL){
253 clicon_err(OE_XML, errno, "malloc");
254 goto done;
255 }
256 snprintf(str, len, "%0lf", xc->xc_number);
257 break;
258 case XT_STRING:
259 if ((str = strdup(xc->xc_string)) == NULL){
260 clicon_err(OE_XML, errno, "strdup");
261 goto done;
262 }
263 break;
264 }
265 *str0 = str;
266 retval = 0;
267 done:
268 return retval;
269 }
270
271 /*! Convert xpath context to number according to number() function in XPATH spec
272 * @param[in] xc XPATH context
273 * @param[out] n0 Floating point or NAN
274 * @retval 0 OK
275 * @retval -1 Error
276 */
277 int
ctx2number(xp_ctx * xc,double * n0)278 ctx2number(xp_ctx *xc,
279 double *n0)
280 {
281 int retval = -1;
282 char *str = NULL;
283 double n;
284
285 switch (xc->xc_type){
286 case XT_NODESET:
287 if (ctx2string(xc, &str) < 0)
288 goto done;
289 if (sscanf(str, "%lf",&n) != 1)
290 n = NAN;
291 break;
292 case XT_BOOL:
293 n = (double)xc->xc_bool;
294 break;
295 case XT_NUMBER:
296 n = xc->xc_number;
297 break;
298 case XT_STRING:
299 if (sscanf(xc->xc_string, "%lf",&n) != 1)
300 n = NAN;
301 break;
302 }
303 *n0 = n;
304 retval = 0;
305 done:
306 if (str)
307 free(str);
308 return retval;
309 }
310
311 /*! Replace a nodeset of a XPATH context with a new nodeset
312 */
313 int
ctx_nodeset_replace(xp_ctx * xc,cxobj ** vec,size_t veclen)314 ctx_nodeset_replace(xp_ctx *xc,
315 cxobj **vec,
316 size_t veclen)
317 {
318 if (xc->xc_nodeset)
319 free(xc->xc_nodeset);
320 xc->xc_nodeset = vec;
321 xc->xc_size = veclen;
322 return 0;
323 }
324
325