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