1 /*
2 ***** BEGIN LICENSE BLOCK *****
3
4 Copyright (C) 2001-2020 Olof Hagsand
5
6 This file is part of CLIgen.
7
8 Licensed under the Apache License, Version 2.0 (the "License");
9 you may not use this file except in compliance with the License.
10 You may obtain a copy of the License at
11
12 http://www.apache.org/licenses/LICENSE-2.0
13
14 Unless required by applicable law or agreed to in writing, software
15 distributed under the License is distributed on an "AS IS" BASIS,
16 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 See the License for the specific language governing permissions and
18 limitations under the License.
19
20 Alternatively, the contents of this file may be used under the terms of
21 the GNU General Public License Version 2 or later (the "GPL"),
22 in which case the provisions of the GPL are applicable instead
23 of those above. If you wish to allow use of your version of this file only
24 under the terms of the GPL, and not to allow others to
25 use your version of this file under the terms of Apache License version 2, indicate
26 your decision by deleting the provisions above and replace them with the
27 notice and other provisions required by the GPL. If you do not delete
28 the provisions above, a recipient may use your version of this file under
29 the terms of any one of the Apache License version 2 or the GPL.
30
31 ***** END LICENSE BLOCK *****
32
33 */
34
35 #include "cligen_config.h"
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <stdint.h>
40 #include <stdarg.h>
41 #include <inttypes.h>
42 #include <unistd.h>
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47 #include <ctype.h>
48 #ifdef HAVE_STRVERSCMP
49 #define _GNU_SOURCE
50 #define __USE_GNU
51 #include <string.h>
52 #undef _GNU_SOURCE
53 #undef __USE_GNU
54 #else /* HAVE_STRVERSCMP */
55 #include <string.h>
56 #endif /* HAVE_STRVERSCMP */
57 #include <errno.h>
58 #include "cligen_buf.h"
59 #include "cligen_cv.h"
60 #include "cligen_cvec.h"
61 #include "cligen_parsetree.h"
62 #include "cligen_pt_head.h"
63 #include "cligen_object.h"
64 #include "cligen_io.h"
65 #include "cligen_read.h"
66 #include "cligen_parse.h"
67 #include "cligen_handle.h"
68 #include "cligen_getline.h"
69
70 /* Private definition of parsetree. Public is defined in cligen_parsetree.h
71 * @see parse_tree_list which is the upper level of a parse-tree
72 */
73 struct parse_tree{
74 struct cg_obj **pt_vec; /* vector of pointers to parse-tree nodes */
75 int pt_len; /* length of vector */
76 #if 1 /* OBSOLETE but keep to after 4.8 */
77 char *pt_name; /* XXX Should be removed, us ph_name instead but eg clixon uses it */
78 #endif
79 char pt_set; /* Parse-tree is a SET */
80 };
81
82 /*! Access function to get the i:th CLIgen object child of a parse-tree
83 * @param[in] pt Parse tree
84 * @param[in] i Which object to return
85 */
86 cg_obj *
pt_vec_i_get(parse_tree * pt,int i)87 pt_vec_i_get(parse_tree *pt,
88 int i)
89 {
90 if (pt == NULL){
91 errno = EINVAL;
92 return NULL;
93 }
94 return pt->pt_vec?pt->pt_vec[i]:NULL;
95 }
96
97 /*! Clear the i:th CLIgen object child of a parse-tree (without freeing existing)
98 * @param[in] pt Parse tree
99 * @param[in] i Which object to return
100 */
101 int
pt_vec_i_clear(parse_tree * pt,int i)102 pt_vec_i_clear(parse_tree *pt,
103 int i)
104 {
105 if (pt == NULL){
106 errno = EINVAL;
107 return -1;
108 }
109 if (i >= pt_len_get(pt)){
110 errno = EINVAL;
111 return -1;
112 }
113 if (pt->pt_vec == NULL){
114 errno = EFAULT;
115 return -1;
116 }
117 pt->pt_vec[i] = NULL;
118 return 0;
119 }
120
121 /*! Insert the i:th CLIgen object child of a parse-tree
122 * @param[in] pt Parse tree
123 * @param[in] i Which position to insert
124 * @param[in] co Object to insert (can be NULL)
125 */
126 int
pt_vec_i_insert(parse_tree * pt,int i,cg_obj * co)127 pt_vec_i_insert(parse_tree *pt,
128 int i,
129 cg_obj *co)
130 {
131 int retval = -1;
132 size_t size;
133
134 if (pt == NULL){
135 errno = EINVAL;
136 goto done;
137 }
138 if (pt_realloc(pt) < 0)
139 goto done;
140 if ((size = (pt_len_get(pt) - (i+1))*sizeof(cg_obj*)) != 0)
141 memmove(&pt->pt_vec[i+1],
142 &pt->pt_vec[i],
143 size);
144 pt->pt_vec[i] = co;
145 retval = 0;
146 done:
147 return retval;
148 }
149
150 int
pt_vec_append(parse_tree * pt,cg_obj * co)151 pt_vec_append(parse_tree *pt,
152 cg_obj *co)
153 {
154 return pt_vec_i_insert(pt, pt_len_get(pt), co);
155 }
156
157 int
pt_vec_i_delete(parse_tree * pt,int i)158 pt_vec_i_delete(parse_tree *pt,
159 int i)
160 {
161 int retval = -1;
162 size_t size;
163 cg_obj *co;
164
165 if (pt == NULL){
166 errno = EINVAL;
167 goto done;
168 }
169 if (i >= pt_len_get(pt)){
170 errno = EINVAL;
171 goto done;
172 }
173 if (pt->pt_vec == NULL){
174 errno = EFAULT;
175 goto done;
176 }
177 co = pt->pt_vec[i];
178 pt->pt_vec[i] = NULL;
179 co_free(co, 1);
180 if ((size = (pt_len_get(pt) - (i+1))*sizeof(cg_obj*)) != 0)
181 memmove(&pt->pt_vec[i],
182 &pt->pt_vec[i+1],
183 size);
184 pt->pt_len--;
185 retval = 0;
186 done:
187 return retval;
188 }
189
190 /*! Access function to get a CLIgen objects child tree vector
191 * @param[in] co CLIgen parse object
192 * @see
193 */
194 int
pt_len_get(parse_tree * pt)195 pt_len_get(parse_tree *pt)
196 {
197 if (pt == NULL){
198 errno = EINVAL;
199 return -1;
200 }
201 return pt->pt_len;
202 }
203
204 #if 1 /* OBSOLETE but keep to after 4.8 */
205 char*
pt_name_get(parse_tree * pt)206 pt_name_get(parse_tree *pt)
207 {
208 if (pt == NULL){
209 errno = EINVAL;
210 return NULL;
211 }
212 return pt->pt_name;
213 }
214
215 int
pt_name_set(parse_tree * pt,char * name)216 pt_name_set(parse_tree *pt,
217 char *name)
218 {
219 if (pt == NULL){
220 errno = EINVAL;
221 return -1;
222 }
223 if (pt->pt_name)
224 free(pt->pt_name);
225 if (name){
226 if ((pt->pt_name = strdup(name)) == NULL)
227 return -1;
228 }
229 else
230 pt->pt_name = NULL;
231 return 0;
232 }
233 #endif /* OBSOLETE but keep to after 4.8 */
234
235 int
pt_sets_get(parse_tree * pt)236 pt_sets_get(parse_tree *pt)
237 {
238 if (pt == NULL){
239 errno = EINVAL;
240 return -1;
241 }
242 return pt->pt_set;
243 }
244 int
pt_sets_set(parse_tree * pt,int sets)245 pt_sets_set(parse_tree *pt,
246 int sets)
247 {
248 if (pt == NULL){
249 errno = EINVAL;
250 return -1;
251 }
252 pt->pt_set = sets;
253 return 0;
254 }
255
256 /*! Allocate a new parsetree
257 * @see pt_free
258 */
259 parse_tree *
pt_new(void)260 pt_new(void)
261 {
262 parse_tree *pt = NULL;
263
264 if ((pt = malloc(sizeof(parse_tree))) == NULL)
265 return NULL;
266 memset(pt, 0, sizeof(parse_tree));
267 return pt;
268 }
269
270 /*! Enlarge the child-vector (pattern) of a parse-tree
271 *
272 * @param[in] pt Cligen object vector
273 * @retval 0 OK
274 * @retval -1 Error
275 * Suppose we have a pattern pt, with lists of cg_obj's 1..4:
276 * pt -> .-.-.-.
277 * | | | |
278 * v v v v
279 * 1 2 3 4
280 * and a new cg_obj 5,
281 * then reallocate pt for another list of cg_objs and copy it into the structure:
282 * pt -> .-.-.-.-.
283 * | | | | |
284 * v v v v v
285 * 1 2 3 4 5
286 */
287 int
pt_realloc(parse_tree * pt)288 pt_realloc(parse_tree *pt)
289 {
290 pt->pt_len++;
291 /* Allocate larger cg_obj vector */
292 if ((pt->pt_vec = realloc(pt->pt_vec, (pt->pt_len)*sizeof(cg_obj *))) == 0)
293 return -1;
294 pt->pt_vec[pt->pt_len - 1] = NULL; /* init field */
295 return 0;
296 }
297
298 /*! Recursively copy a parse-tree.
299 *
300 * No common pointers between the two structures
301 *
302 * @param[in] pt Original parse-tree
303 * @param[in] parent The parent of the new parsetree. Need not be same as parent of the orignal
304 * @param[out] ptn New parse-tree (need to be already created on entry)
305 * @retval 0 OK
306 * @retval -1 Error
307 * @see pt_dup
308 */
309 int
pt_copy(parse_tree * pt,cg_obj * co_parent,parse_tree * ptn)310 pt_copy(parse_tree *pt,
311 cg_obj *co_parent,
312 parse_tree *ptn)
313 {
314 int retval = -1;
315 int i;
316 int j;
317 cg_obj *co;
318
319 if (pt == NULL || ptn == NULL){
320 errno = EINVAL;
321 goto done;
322 }
323 /* subtract treereferences, which are instances of other trees */
324 for (i=0; i<pt_len_get(pt); i++){
325 if ((co = pt_vec_i_get(pt,i)) && co_flags_get(co, CO_FLAGS_TREEREF))
326 ;
327 else
328 ptn->pt_len++;
329 }
330 if (pt_len_get(ptn) &&
331 (ptn->pt_vec = (cg_obj **)malloc(pt_len_get(ptn)*sizeof(cg_obj *))) == NULL){
332 fprintf(stderr, "%s: malloc: %s\n", __FUNCTION__, strerror(errno));
333 goto done;
334 }
335 j=0;
336 for (i=0; i<pt_len_get(pt); i++){
337 if ((co = pt_vec_i_get(pt, i)) != NULL){
338 if (!co_flags_get(co, CO_FLAGS_TREEREF))
339 if (co_copy(co, co_parent, &ptn->pt_vec[j++]) < 0)
340 goto done;
341 }
342 else
343 ptn->pt_vec[j++] = NULL;
344 }
345 retval = 0;
346 done:
347 return retval;
348 }
349
350 /*! Duplicate a parse-tree recursively
351 *
352 * @param[in] pt Original parse-tree
353 * @param[in] parent The parent of the new parsetree. Need not be same as parent of the orignal
354 * @retval ptnp New parse-tree
355 * @retval NULL Error
356 */
357 parse_tree *
pt_dup(parse_tree * pt,cg_obj * cop)358 pt_dup(parse_tree *pt,
359 cg_obj *cop)
360 {
361 parse_tree *ptn = NULL;
362
363 if (pt == NULL){
364 errno = EINVAL;
365 goto done;
366 }
367 if ((ptn = pt_new()) == NULL)
368 goto done;
369 if (pt_copy(pt, cop, ptn) < 0){
370 ptn = NULL;
371 goto done;
372 }
373 done:
374 return ptn;
375 }
376
377 /*! Recursively merge two parse-trees: pt1 into pt0
378 * @param[in,out] pt0 parse-tree 0. On exit contains pt1 too
379 * @param[in] parent Parent of pt0
380 * @param[in] pt1 parse-tree 1. Merge this into pt0
381 * @retval 0 OK
382 * @retval -1 Error
383 */
384 int
cligen_parsetree_merge(parse_tree * pt0,cg_obj * parent,parse_tree * pt1)385 cligen_parsetree_merge(parse_tree *pt0,
386 cg_obj *parent,
387 parse_tree *pt1)
388 {
389 cg_obj *co0=NULL;
390 cg_obj *co1;
391 cg_obj *co1c;
392 int i;
393 int j;
394 int retval = -1;
395 int exist;
396
397 for (j=0; j<pt_len_get(pt1); j++){
398 co1 = pt_vec_i_get(pt1, j);
399 exist = 0;
400 for (i=0; i<pt_len_get(pt0); i++){
401 co0 = pt_vec_i_get(pt0, i);
402 if (co0 == NULL && co1 == NULL){
403 exist = 1;
404 break;
405 }
406 if (co0 && co1 && co_eq(co0, co1)==0){
407 if (co0->co_callbacks == NULL && co1->co_callbacks != NULL){
408 /* Cornercase: co0 callback is NULL and co1 callback is not
409 * Copy from co1 to co0
410 */
411 if (co_callback_copy(co1->co_callbacks, &co0->co_callbacks) < 0)
412 goto done;
413 }
414 exist = 1;
415 break;
416 }
417 }
418 if (co1==NULL){ /* empty */
419 if (exist)
420 continue;
421 if (pt_realloc(pt0) < 0)
422 goto done;
423 pt0->pt_vec[pt_len_get(pt0)-1] = NULL;
424 continue;
425 }
426 if (exist){
427 if (cligen_parsetree_merge(co_pt_get(co0), co0, co_pt_get(co1)) < 0)
428 goto done;
429 }
430 else{
431 if (pt_realloc(pt0) < 0)
432 goto done;
433 if (co_copy(co1, parent, &co1c) < 0)
434 goto done;
435 pt0->pt_vec[pt_len_get(pt0)-1] = co1c;
436 }
437 }
438 cligen_parsetree_sort(pt0, 0);
439 retval = 0;
440 done:
441 return retval;
442 }
443
444 /*! Help function to qsort for sorting entries in pattern file.
445 * @param[in] arg1
446 * @param[in] arg2
447 * @retval 0 If equal
448 * @retval <0 if arg1 is less than arg2
449 * @retval >0 if arg1 is greater than arg2
450 */
451 static int
co_cmp(const void * arg1,const void * arg2)452 co_cmp(const void* arg1,
453 const void* arg2)
454 {
455 cg_obj* co1 = *(cg_obj**)arg1;
456 cg_obj* co2 = *(cg_obj**)arg2;
457
458 if (co1 == NULL){
459 if (co2 == NULL)
460 return 0;
461 else
462 return -1;
463 }
464 else if (co2 == NULL)
465 return 1;
466 else
467 return co_eq(co1, co2);
468 }
469
470 /*! Sort CLIgen parse-tree, optionally recursive
471 * @param[in] The CLIgen parse-tree
472 * @param[in] recursive. If set sort recursive calls
473 * @retval void
474 */
475 void
cligen_parsetree_sort(parse_tree * pt,int recursive)476 cligen_parsetree_sort(parse_tree *pt,
477 int recursive)
478 {
479 cg_obj *co;
480 int i;
481 parse_tree *pt1;
482
483 qsort(pt->pt_vec, pt_len_get(pt), sizeof(cg_obj*), co_cmp);
484 for (i=0; i<pt_len_get(pt); i++){
485 if ((co = pt_vec_i_get(pt, i)) == NULL)
486 continue;
487 if (co_flags_get(co, CO_FLAGS_MARK) == 0){ /* not recursive */
488 co_flags_set(co, CO_FLAGS_MARK);
489 pt1 = co_pt_get(co);
490 if (pt1 && recursive){
491 cligen_parsetree_sort(pt1, 1);
492 }
493 co_flags_reset(co, CO_FLAGS_MARK);
494 }
495 }
496 }
497
498 /*! Free all parse-tree nodes of the parse-tree,
499 * @param[in] pt CLIgen parse-tree
500 * @param[in] recursive If set free recursive
501 * @retval 0 OK
502 * @retval -1 Error
503 */
504 int
pt_free(parse_tree * pt,int recursive)505 pt_free(parse_tree *pt,
506 int recursive)
507 {
508 int i;
509 cg_obj *co;
510
511 if (pt == NULL){
512 errno = EINVAL;
513 return -1;
514 }
515 if (pt->pt_vec != NULL){
516 for (i=0; i<pt_len_get(pt); i++)
517 if ((co = pt_vec_i_get(pt, i)) != NULL)
518 co_free(co, recursive);
519 free(pt->pt_vec);
520 }
521 pt->pt_len = 0;
522 if (pt->pt_name){
523 free(pt->pt_name);
524 pt->pt_name = NULL;
525 }
526 free(pt);
527 return 0;
528 }
529
530 int
cligen_parsetree_free(parse_tree * pt,int recursive)531 cligen_parsetree_free(parse_tree *pt,
532 int recursive)
533 {
534 return pt_free(pt, recursive);
535 }
536
537 /*! Apply a function call recursively on all cg_obj:s in a parse-tree
538 *
539 * Recursively traverse all cg_obj in a parse-tree and apply fn(arg) for each
540 * object found. The function is called with the cg_obj and an argument as args.
541 * @param[in] pt CLIgen parse-tree
542 * @param[in] fn Function to apply
543 * @param[in] arg Argument to function
544 * @retval 0 OK (all applied function calls return 0)
545 * @retval -1 Error (one applied function call return -1)
546 */
547 int
pt_apply(parse_tree * pt,cg_applyfn_t fn,void * arg)548 pt_apply(parse_tree *pt,
549 cg_applyfn_t fn,
550 void *arg)
551 {
552 cg_obj *co;
553 int i;
554 int retval = -1;
555
556 if (pt->pt_vec == NULL)
557 return 0;
558 for (i=0; i<pt_len_get(pt); i++){
559 if ((co = pt_vec_i_get(pt, i)) == NULL)
560 continue;
561 if (fn(co, arg) < 0)
562 goto done;
563 if (pt_apply(co_pt_get(co), fn, arg) < 0)
564 goto done;
565 }
566 retval = 0;
567 done:
568 return retval;
569 }
570
571