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