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 /* Access macro */
71 cg_obj*
co_up(cg_obj * co)72 co_up(cg_obj *co)
73 {
74     return co->co_prev;
75 }
76 
77 int
co_up_set(cg_obj * co,cg_obj * cop)78 co_up_set(cg_obj *co, cg_obj *cop)
79 {
80     co->co_prev = cop;
81     return 0;
82 }
83 
84 /*! return top-of-tree (ancestor) */
85 cg_obj*
co_top(cg_obj * co0)86 co_top(cg_obj *co0)
87 {
88     cg_obj *co = co0;
89     cg_obj *co1;
90 
91     while ((co1 = co_up(co)) != NULL)
92 	co = co1;
93     return co;
94 }
95 
96 static int
co_pt_realloc(cg_obj * co)97 co_pt_realloc(cg_obj *co)
98 {
99     int retval = -1;
100     if (co->co_pt_len == 0){
101 	co->co_pt_len++;
102 	//	if ((co->co_ptvec = realloc(co->co_ptvec, (co->co_pt_len)*sizeof(parse_tree *))) == 0)
103 	if ((co->co_ptvec = calloc(co->co_pt_len, sizeof(parse_tree *))) == 0)
104 	    goto done;
105     }
106     retval = 0;
107  done:
108     return retval;
109 }
110 
111 /*! Access function to get a CLIgen objects parse-tree head
112  * @param[in]  co  CLIgen parse object
113  * @retval     pt   parse-tree
114  * @retval     NULL Error or no such parsetree
115  */
116 parse_tree *
co_pt_get(cg_obj * co)117 co_pt_get(cg_obj *co)
118 {
119     if (co == NULL){
120        errno = EINVAL;
121        return NULL;
122     }
123     return co->co_ptvec?co->co_ptvec[0]:NULL;
124 }
125 
126 /*! Set a CLIgen objects parse-tree head
127  * @param[in]  co  CLIgen parse object
128  * @param[in]  pt  CLIgen parse tree
129  * @retval     0   OK
130  * @retval    -1   Error
131  */
132 int
co_pt_set(cg_obj * co,parse_tree * pt)133 co_pt_set(cg_obj     *co,
134 	  parse_tree *pt)
135 {
136     if (co == NULL){
137        errno = EINVAL;
138        return -1;
139     }
140     if (co->co_pt_len == 0){
141 	if (co_pt_realloc(co) < 0)
142 	    return -1;
143     }
144     else {
145 	if (co->co_ptvec[0])
146 	    pt_free(co->co_ptvec[0], 1);
147     }
148     co->co_ptvec[0] = pt;
149     return 0;
150 }
151 
152 /*! Clear a CLIgen objects parse-tree head (dont free old)
153  * @param[in]  co  CLIgen parse object
154  * @param[in]  pt  CLIgen parse tree
155  * @retval     0   OK
156  * @retval    -1   Error
157  * @see co_pt_set
158  */
159 int
co_pt_clear(cg_obj * co)160 co_pt_clear(cg_obj *co)
161 {
162     if (co == NULL){
163        errno = EINVAL;
164        return -1;
165     }
166     if (co->co_pt_len == 0){
167 	if (co_pt_realloc(co) < 0)
168 	    return -1;
169     }
170     co->co_ptvec[0] = NULL;
171     return 0;
172 }
173 
174 void
co_flags_set(cg_obj * co,uint32_t flag)175 co_flags_set(cg_obj  *co,
176 	     uint32_t flag)
177 {
178     co->co_flags |= flag;
179 }
180 
181 void
co_flags_reset(cg_obj * co,uint32_t flag)182 co_flags_reset(cg_obj  *co,
183 	       uint32_t flag)
184 {
185     co->co_flags &= ~flag;
186 }
187 
188 int
co_flags_get(cg_obj * co,uint32_t flag)189 co_flags_get(cg_obj  *co,
190 	     uint32_t flag)
191 {
192     return (co->co_flags & flag) ? 1 : 0;
193 }
194 
195 int
co_sets_get(cg_obj * co)196 co_sets_get(cg_obj *co)
197 {
198     parse_tree *pt;
199 
200     if ((pt = co_pt_get(co)) != NULL)
201 	return 0;
202     else
203 	return pt_sets_get(pt);
204 }
205 
206 void
co_sets_set(cg_obj * co,int sets)207 co_sets_set(cg_obj *co,
208 	    int     sets)
209 
210 {
211     parse_tree *pt;
212     if ((pt = co_pt_get(co)) != NULL)
213 	pt_sets_set(pt, sets);
214 }
215 
216 /*! Assign a preference to a cligen variable object
217  * Prefer more specific commands/variables  if you have to choose from several.
218  * @param[in] co   Cligen obe
219  * @retval    pref Preference: positive integer
220  * The preference is (higher more preferred):
221  * command / keyword
222  * (ip|mac)
223  * decimal64
224  * int8 with range , uint8 with range
225  * ...
226  * int64 with range , uint64 with range
227  * int8 , uint8
228  * ...
229  * int64 , uint64
230  * interface
231  * regexp
232  * string
233  * rest
234  * Note in a choice: <int32>|<uint16>, uint16 is preferred.
235  * XXX: It does not cover: <int32 range:0-12>|<int32 range:6-18>
236  */
237 static int
cov_pref(cg_obj * co)238 cov_pref(cg_obj *co)
239 {
240     int pref = 0;
241 
242     switch (co->co_vtype){
243     case CGV_ERR:
244 	pref = 0; /* Illegal */
245 	break;
246 	/* ints in range 22-60 */
247     case CGV_INT8:
248 	if (co->co_rangelen)
249 	    pref = 60;
250 	else
251 	    pref = 52;
252 	break;
253     case CGV_INT16:
254 	if (co->co_rangelen)
255 	    pref = 58;
256 	else
257 	    pref = 50;
258 	break;
259     case CGV_INT32:
260 	if (co->co_rangelen)
261 	    pref = 56;
262 	else
263 	    pref = 48;
264 	break;
265     case CGV_INT64:
266 	if (co->co_rangelen)
267 	    pref = 54;
268 	else
269 	    pref = 46;
270 	break;
271     case CGV_UINT8:
272 	if (co->co_rangelen)
273 	    pref = 59;
274 	else
275 	    pref = 51;
276 	break;
277     case CGV_UINT16:
278 	if (co->co_rangelen)
279 	    pref = 57;
280 	else
281 	    pref = 49;
282 	break;
283     case CGV_UINT32:
284 	if (co->co_rangelen)
285 	    pref = 55;
286 	else
287 	    pref = 47;
288 	break;
289     case CGV_UINT64:
290 	if (co->co_rangelen)
291 	    pref = 53;
292 	else
293 	    pref = 45;
294 	break;
295     case CGV_DEC64:
296 	pref = 62;
297 	break;
298     case CGV_BOOL:
299 	pref = 12;
300 	break;
301     case CGV_REST:
302 	pref = 1;
303 	break;
304     case CGV_STRING:
305 	if (co->co_regex)
306 	    pref = 7;
307 	else
308 	    pref = 5;
309 	break;
310     case CGV_INTERFACE:
311 	pref = 10;
312 	break;
313     case CGV_IPV4ADDR:
314     case CGV_IPV4PFX:
315 	pref = 70;
316 	break;
317     case CGV_IPV6ADDR:
318     case CGV_IPV6PFX:
319 	pref = 71;
320 	break;
321     case CGV_MACADDR:
322 	pref = 72;
323 	break;
324     case CGV_URL:
325 	pref = 20;
326 	break;
327     case CGV_UUID:
328 	pref = 73;
329 	break;
330     case CGV_TIME:
331 	pref = 74;
332 	break;
333     case CGV_VOID: /* N/A */
334 	break;
335     case CGV_EMPTY:
336 	break;
337     }
338     return pref;
339 }
340 
341 /*! Assign a preference to a cligen object
342  * @param[in]  co    cligen_object
343  * @param[in]  exact if match was exact (only applies to CO_COMMAND)
344  * @retval     pref  Preference: positive integer
345  *
346  * The higher the better
347  * if you have to choose from several.
348  * The preference is:
349  * command > ip|mac > int > interface > string > expand > rest
350  * 'expand' is a command with not exact match that is derived from a <expand> or <choice>
351  */
352 int
co_pref(cg_obj * co,int exact)353 co_pref(cg_obj *co,
354 	int     exact)
355 {
356     int pref = 0;;
357 
358     switch (co->co_type){
359     case CO_COMMAND:
360 	if (co->co_ref && !exact)
361 	    pref = 3; /* expand */
362 	else
363 	    pref = 100;
364 	break;
365     case CO_VARIABLE:
366 	pref = cov_pref(co);
367 	break;
368     case CO_REFERENCE: /* ? */
369 
370 	break;
371     }
372     return pref;
373 }
374 
375 /*! Just malloc a CLIgen object. No other allocations allowed */
376 cg_obj *
co_new_only()377 co_new_only()
378 {
379     cg_obj *co;
380 
381     if ((co = malloc(sizeof(cg_obj))) == NULL)
382 	return NULL;
383     memset(co, 0, sizeof(cg_obj));
384     return co;
385 }
386 
387 /*! Create new cligen parse-tree command object
388  *
389  * That is, a cligen parse-tree object with type == CO_COMMAND (not variable)
390  * @param[in]  cmd   Initial command value
391  * @param[in]  prev  parent object (or NULL)
392  * @retval     NULL  Error
393  * @retval     co    Created cligen object. Free with co_free()
394  * @see cov_new
395  * @see co_free
396  */
397 cg_obj *
co_new(char * cmd,cg_obj * parent)398 co_new(char   *cmd,
399        cg_obj *parent)
400 {
401     cg_obj     *co;
402     parse_tree *pt;
403 
404     if ((co = co_new_only()) == NULL)
405 	return NULL;
406     co->co_type    = CO_COMMAND;
407     if (cmd)
408 	co->co_command = strdup(cmd);
409     co_up_set(co, parent);
410     /* parse-tree created implicitly */
411     if ((pt = pt_new()) == NULL)
412 	return NULL;
413     if (co_pt_set(co, pt) < 0)
414 	return NULL;
415     return co;
416 }
417 
418 /*! Create new cligen parse-tree variable object
419  *
420  * That is, a cligen parse-tree object with type == CO_VARIABLE
421  * @param[in]  cvtype  Cligen variable type
422  * @param[in]  parent  parent object (or NULL)
423  * @retval     NULL    Error
424  * @retval     co      Created cligen object. Free with co_free()
425  * @see co_new
426  * @see co_free
427  */
428 cg_obj *
cov_new(enum cv_type cvtype,cg_obj * parent)429 cov_new(enum cv_type cvtype,
430 	cg_obj      *parent)
431 {
432     cg_obj     *co;
433     parse_tree *pt;
434 
435     if ((co = co_new_only()) == NULL)
436 	return NULL;
437     co->co_type    = CO_VARIABLE;
438     co->co_vtype   = cvtype;
439     if (parent)
440 	co_up_set(co, parent);
441     co->co_dec64_n = CGV_DEC64_N_DEFAULT;
442     /* parse-tree created implicitly */
443     if ((pt = pt_new()) == NULL)
444 	return NULL;
445     if (co_pt_set(co, pt) < 0)
446 	return NULL;
447     return co;
448 }
449 
450 /*! Copy a linked list of cg_obj callback objects
451  *
452  * Copy a linked list of cg_obj callback objects, including function pointer,
453  * function name,
454  *
455  * @param[in]  cc0  The object to copy from
456  * @param[out] ccn  Pointer to the object to copy to (is allocated)
457  * @param[in]  cgv  if given, is a string that overrides the arg in cc.
458  * @retval     0      OK
459  * @retval     -1     Error
460  */
461 int
co_callback_copy(struct cg_callback * cc0,struct cg_callback ** ccn)462 co_callback_copy(struct cg_callback  *cc0,
463 		 struct cg_callback **ccn)
464 {
465     struct cg_callback  *cc;
466     struct cg_callback  *cc1;
467     struct cg_callback **ccp;
468 
469     ccp = ccn;
470     for (cc = cc0; cc; cc=cc->cc_next){
471 	if ((cc1 = malloc(sizeof(*cc1))) == NULL){
472 	    fprintf(stderr, "%s: malloc: %s\n", __FUNCTION__, strerror(errno));
473 	    return -1;
474 	}
475 	memset(cc1, 0, sizeof(*cc1));
476 	cc1->cc_fn_vec = cc->cc_fn_vec;
477 	if (cc->cc_fn_str)
478 	    if ((cc1->cc_fn_str = strdup(cc->cc_fn_str)) == NULL){
479 		fprintf(stderr, "%s: strdup: %s\n", __FUNCTION__, strerror(errno));
480 		return -1;
481 	    }
482 	if (cc->cc_cvec && ((cc1->cc_cvec = cvec_dup(cc->cc_cvec)) == NULL))
483 	    return -1;
484 	*ccp = cc1;
485 	ccp = &cc1->cc_next;
486     }
487     return 0;
488 }
489 
490 /*! Recursively copy a cligen object.
491  *
492  * @param[in]  co     The object to copy from
493  * @param[in]  parent The parent of the new object, need not be same as parent of co
494  * @param[out] conp   Pointer to the object to copy to (is allocated)
495  * @retval     0      OK
496  * @retval     -1     Error
497  * @see co_expand_sub
498  */
499 int
co_copy(cg_obj * co,cg_obj * parent,cg_obj ** conp)500 co_copy(cg_obj  *co,
501 	cg_obj  *parent,
502 	cg_obj **conp)
503 {
504     int         retval = -1;
505     cg_obj     *con = NULL;
506     parse_tree *pt;
507     parse_tree *ptn;
508 
509     if ((con = co_new_only()) == NULL)
510 	goto done;
511     memcpy(con, co, sizeof(cg_obj));
512     con->co_ptvec = NULL;
513     con->co_pt_len = 0;
514     con->co_ref = NULL;
515     if (co->co_treeref_orig)
516 	con->co_treeref_orig = co->co_treeref_orig;
517     else
518 	con->co_treeref_orig = co;
519     co_flags_reset(con, CO_FLAGS_MARK);
520     co_flags_reset(con, CO_FLAGS_REFDONE);
521     /* Replace all pointers */
522     co_up_set(con, parent);
523     if (co->co_command)
524 	if ((con->co_command = strdup(co->co_command)) == NULL)
525 	    goto done;
526     if (co_callback_copy(co->co_callbacks, &con->co_callbacks) < 0)
527 	goto done;
528     if (co->co_cvec)
529 	con->co_cvec = cvec_dup(co->co_cvec);
530     if ((pt = co_pt_get(co)) != NULL){
531 	if ((ptn = pt_dup(pt, con)) == NULL) /* sets a new pt under con */
532 	    goto done;
533 	if (co_pt_set(con, ptn) < 0)
534 	    goto done;
535     }
536 #ifdef CO_HELPVEC
537     if (co->co_helpvec)
538 	if ((con->co_helpvec = cvec_dup(co->co_helpvec)) == NULL)
539 	    goto done;
540 #else
541     if (co->co_help)
542 	if ((con->co_help = strdup(co->co_help)) == NULL)
543 	    goto done;
544 #endif
545     if (co_value_set(con, co->co_value) < 0) /* XXX: free p� co->co_value? */
546 	goto done;
547     if (co->co_type == CO_VARIABLE){
548 	if (co->co_expand_fn_str)
549 	    if ((con->co_expand_fn_str = strdup(co->co_expand_fn_str)) == NULL)
550 		goto done;
551 	if (co->co_translate_fn_str)
552 	    if ((con->co_translate_fn_str = strdup(co->co_translate_fn_str)) == NULL)
553 		goto done;
554 	if (co->co_show)
555 	    if ((con->co_show = strdup(co->co_show)) == NULL)
556 		goto done;
557 	if (co->co_rangecvv_low)
558 	    if ((con->co_rangecvv_low = cvec_dup(co->co_rangecvv_low)) == NULL)
559 		goto done;
560 	if (co->co_rangecvv_upp)
561 	    if ((con->co_rangecvv_upp = cvec_dup(co->co_rangecvv_upp)) == NULL)
562 		goto done;
563 	if (co->co_expand_fn_vec)
564 	    if ((con->co_expand_fn_vec = cvec_dup(co->co_expand_fn_vec)) == NULL)
565 		goto done;
566 	if (co->co_choice){
567 	    if ((con->co_choice = strdup(co->co_choice)) == NULL)
568 		goto done;
569 	}
570 	if (co->co_regex){
571 	    if ((con->co_regex = cvec_dup(co->co_regex)) == NULL)
572 		goto done;
573 	}
574     } /* VARIABLE */
575     *conp = con;
576     retval = 0;
577  done:
578     return retval;
579 }
580 
581 /*! Compare two strings, extends strcmp
582  * Basically strcmp but there are some complexities which one may enable.
583  * Also handles NULL (NULL < all strings)
584  * @param[in]  s1
585  * @param[in]  s2
586  * @retval  0  equal
587  * @retval <0  str1 is less than str2
588  * @retval >0  str1 is greater than str2
589  *
590  * strcmp orders:  1 10 2
591  * wheras strverscmp orders: 1 2 10
592  * but:
593  * strcmp orders:  1b 16 6b
594  * wheras strverscmp orders: 1b 6b 16
595  * If we use strverscmp we also must use it in e.g. complete
596  */
597 static inline int
str_cmp(char * s1,char * s2)598 str_cmp(char *s1,
599 	char *s2)
600 {
601     if (s1 == NULL && s2 == NULL)
602 	return 0;
603     if (s1 == NULL) /* empty string first */
604 	return -1;
605     if (s2 == NULL)
606 	return 1;
607     /*
608      * XXX: the cligen handler code uses NULL here which is wrong, but those
609      * options are for now global settings.
610      */
611 #ifdef  HAVE_STRVERSCMP
612     if (cligen_lexicalorder(NULL))
613 	return strverscmp(s1, s2); /* can't combine lexicalorder and ignorecase */
614     else
615 	return cligen_ignorecase(NULL) ? strcasecmp(s1, s2) : strcmp(s1, s2);
616 #else /* HAVE_STRVERSCMP */
617     return cligen_ignorecase(NULL) ? strcasecmp(s1, s2) : strcmp(s1, s2);
618 #endif /* HAVE_STRVERSCMP */
619 }
620 
621 /*! Check if two cligen objects (cg_obj) ar equal
622  *
623  * Two cligen objects are equal if they have:
624  * - same type (variable or command)
625  * - if command, they should have same command name
626  * - if they are a variable, they should also have:
627  *          + same variable type.
628  *          + same expand, choice, range and regexp options.
629  * @param[in]  co1
630  * @param[in]  co2
631  * @retval  0  If equal
632  * @retval <0  if co1 is less than co2
633  * @retval >0  if co1 is greater than co2
634  * @see str_cmp
635  */
636 int
co_eq(cg_obj * co1,cg_obj * co2)637 co_eq(cg_obj *co1,
638       cg_obj *co2)
639 {
640     int eq;
641 
642     /* eq == 0 means equal */
643     eq = !(co1->co_type == co2->co_type);
644     if (eq){ /* Unequal type of command, variable and reference.
645 		but need to check special case,
646 		if the variable is a KEYWORD, then it can be eq to a command. */
647 	/* Let References be last (more than) everything else */
648 	if (co1->co_type == CO_REFERENCE)
649 	    eq = 1;
650 	if (co2->co_type == CO_REFERENCE)
651 	    eq = -1;
652 	/* Here one is command and one is variable */
653 	if (co1->co_type == CO_COMMAND){
654 	    eq = strcmp(co1->co_command, co2->co_command);
655 	    goto done;
656 	}
657 	else{ 	    /* co2->co_type == CO_COMMAND */
658 	    eq = strcmp(co1->co_command, co2->co_command);
659 	    goto done;
660 	}
661 	/* shouldnt reach */
662 	goto done;
663     }
664     switch (co1->co_type){
665     case CO_COMMAND:
666     case CO_REFERENCE:
667 	eq = str_cmp(co1->co_command, co2->co_command);
668 	break;
669     case CO_VARIABLE:
670 	eq = (co1->co_vtype == co2->co_vtype)?0:(co1->co_vtype < co2->co_vtype)?-1:1;
671 	/* Same variable type */
672 	if (eq != 0)
673 	    goto done;
674 	/* Examine expand: at least one set, and then strcmp */
675 	if (co1->co_expand_fn_str!=NULL || co2->co_expand_fn_str!=NULL){
676 	    eq = str_cmp(co1->co_expand_fn_str, co2->co_expand_fn_str);
677 	    goto done;
678 	}
679 	/* Should we examine co_translate_fn_str? */
680 	/* Examine choice: at least one set, and then strcmp */
681 	if (co1->co_choice!=NULL || co2->co_choice!=NULL){
682 	    eq = str_cmp(co1->co_choice, co2->co_choice);
683 	    goto done;
684 	}
685 	/* Examine regexp, at least one set, and then strcmp */
686 	if (co1->co_regex!=NULL || co2->co_regex!=NULL){
687 	    cg_var *cv1, *cv2;
688 	    if (co1->co_regex == NULL)
689 		eq = -1;
690 	    else if (co2->co_regex == NULL)
691 		eq = 1;
692 	    else{
693 		int i, min;
694 		min = cvec_len(co1->co_regex)<cvec_len(co2->co_regex)?cvec_len(co1->co_regex):cvec_len(co2->co_regex);
695 		for (i=0; i<min; i++){
696 		    cv1 = cvec_i(co1->co_regex, i);
697 		    cv2 = cvec_i(co2->co_regex, i);
698 		    if ((eq = str_cmp(cv_string_get(cv1), cv_string_get(cv2))) != 0)
699 			goto done;
700 		}
701 		if (cvec_len(co1->co_regex) < cvec_len(co2->co_regex))
702 		    eq = -1;
703 		else if (cvec_len(co1->co_regex) > cvec_len(co2->co_regex))
704 		    eq = 1;
705 		else
706 		    eq = 0;
707 	    }
708 	    if (eq)
709 		goto done;
710 	}
711 	/* Examine int and range */
712 	if (cv_isint(co1->co_vtype) || cv_isstring(co1->co_vtype)) {
713 	    int i;
714 	    cg_var *cv1, *cv2;
715 	    if ((eq = co1->co_rangelen - co2->co_rangelen) != 0)
716 		goto done;
717 	    /* either both 0 or both same length */
718 	    for (i=0; i<co1->co_rangelen; i++){
719 		cv1 = cvec_i(co1->co_rangecvv_low, i);
720 		cv2 = cvec_i(co2->co_rangecvv_low, i);
721 		if ((eq = cv_cmp(cv1, cv2)) != 0)
722 			goto done;
723 		cv1 = cvec_i(co1->co_rangecvv_upp, i);
724 		cv2 = cvec_i(co2->co_rangecvv_upp, i);
725 		if ((eq = cv_cmp(cv1, cv2)) != 0)
726 			goto done;
727 	    }
728 	} /* range */
729 	break;
730     }
731   done:
732     return eq;
733 }
734 
735 /*! Free an individual syntax node (cg_obj).
736  * @param[in]  co         CLIgen object
737  * @param[in]  recursive  If set free recursive, if 0 free only cligen object, and parsetree
738  * @retval     0          OK
739  * @retval    -1          Error
740  * Note that the co_var pointer is not freed. The application
741  * needs to handle it (dont use a pointer to the stack for example).
742  * Note: if you add a free here, you should probably add somthing in
743  * co_copy and co_expand_sub
744  */
745 int
co_free(cg_obj * co,int recursive)746 co_free(cg_obj *co,
747 	int     recursive)
748 {
749     struct cg_callback *cc;
750     parse_tree         *pt;
751 
752 #ifdef CO_HELPVEC
753     if (co->co_helpvec)
754 	cvec_free(co->co_helpvec);
755 #else
756     if (co->co_help)
757 	free(co->co_help);
758 #endif
759     if (co->co_command)
760 	free(co->co_command);
761     if (co->co_value)
762 	free(co->co_value);
763     if (co->co_cvec)
764 	cvec_free(co->co_cvec);
765     while ((cc = co->co_callbacks) != NULL){
766 	if (cc->cc_cvec)
767 	    cvec_free(cc->cc_cvec);
768 	if (cc->cc_fn_str)
769 	    free(cc->cc_fn_str);
770 	co->co_callbacks = cc->cc_next;
771 	free(cc);
772     }
773     if (co->co_type == CO_VARIABLE){
774 	if (co->co_expand_fn_str)
775 	    free(co->co_expand_fn_str);
776 	if (co->co_translate_fn_str)
777 	    free(co->co_translate_fn_str);
778 	if (co->co_show)
779 	    free(co->co_show);
780 	if (co->co_expand_fn_vec)
781 	    cvec_free(co->co_expand_fn_vec);
782 	if (co->co_choice)
783 	    free(co->co_choice);
784 	if (co->co_regex)
785 	    cvec_free(co->co_regex);
786 	if (co->co_rangecvv_low)
787 	    cvec_free(co->co_rangecvv_low);
788 	if (co->co_rangecvv_upp)
789 	    cvec_free(co->co_rangecvv_upp);
790     }
791     if (recursive && (pt = co_pt_get(co)) != NULL){
792 	pt_free(pt, 1); /* recursive */
793     }
794     if (co->co_ptvec != NULL)
795 	free(co->co_ptvec);
796     free(co);
797     return 0;
798 }
799 
800 /*! Look for a CLIgen object in a (one-level) parse-tree in interval [low,high]
801  * @param[in]  pt      CLIgen parse-tree
802  * @param[in]  name    Name of node
803  * @param[in]  low     Lower bound
804  * @param[in]  upper   Upper bound
805  * @retval     co      Object found
806  * @retval     NULL    Not found
807  * @see co_insert Main function
808  */
809 static cg_obj *
co_search1(parse_tree * pt,char * name,int low,int upper)810 co_search1(parse_tree *pt,
811 	   char       *name,
812 	   int         low,
813 	   int         upper)
814 {
815     int     mid;
816     int     cmp;
817     cg_obj *co;
818 
819     if (upper < low)
820 	return NULL; /* not found */
821     mid = (low + upper) / 2;
822     if (mid >= pt_len_get(pt))  /* beyond range */
823 	return NULL;
824     co = pt_vec_i_get(pt, mid);
825     cmp = str_cmp(name, co ? co->co_command : NULL);
826     if (cmp < 0)
827 	return co_search1(pt, name, low, mid-1);
828     else if (cmp > 0)
829 	return co_search1(pt, name, mid+1, upper);
830     else
831 	return co;
832 }
833 
834 /*! Position where to insert cligen object into a parse-tree list alphabetically
835  * at what position to insert co1. Insert after this position
836  * @param[in]  pt        CLIgen parse-tree
837  * @param[in]  co1       CLIgen object to insert
838  * @param[in]  low       Lower bound
839  * @param[in]  upper     Upper bound (+1)
840  * @retval     position
841  * @see co_insert Main function
842  */
843 static int
co_insert_pos(parse_tree * pt,cg_obj * co1,int low,int upper)844 co_insert_pos(parse_tree *pt,
845 	      cg_obj     *co1,
846 	      int         low,
847 	      int         upper)
848 {
849     int     mid;
850     int     cmp;
851     cg_obj *co2; /* variable for objects in list */
852 
853     if (upper < low)
854 	return low; /* not found */
855     mid = (low + upper) / 2;
856     if (mid >= pt_len_get(pt))
857 	return pt_len_get(pt);
858     if (co1 == NULL)
859 	return 0; /* Insert in 1st pos */
860     co2 = pt_vec_i_get(pt, mid);
861     if (co2 == NULL)
862 	cmp = 1;
863     else
864 	cmp = co_eq(co1, co2); /* -1 if co1 < co2,.. */
865     if (cmp < 0)
866 	return co_insert_pos(pt, co1, low, mid-1);
867     else if (cmp > 0)
868 	return co_insert_pos(pt, co1, mid+1, upper);
869     else
870 	return mid;
871 }
872 
873 /*! Add a cligen object (co1) to a parsetree(pt) alphabetically.
874  * This involves searching in the parsetree for the position where it should be added,
875  * Then checking whether an equivalent version already exists.
876  * Then modifying the parsetree by shifting it down, and adding the new object.
877  * There is som complexity if co == NULL.
878  * @param[in] pt   Parse-tree
879  * @param[in] co1  CLIgen object
880  * @retval    co   object if found (old _or_ new). NOTE: you must replace calling
881  *                 cg_obj with return.
882  * @retval    NULL error
883  * @note co1 maye be deleted in this call. Dont use co after this call,use retval
884  * XXX: pt=[a b] + co1=[b] -> [a b] but children of one b is lost,..
885  */
886 cg_obj*
co_insert(parse_tree * pt,cg_obj * co1)887 co_insert(parse_tree *pt,
888 	  cg_obj     *co1)
889 {
890     int     pos;
891     cg_obj *co2;
892 
893     /* find closest to co in parsetree, insert after pos. */
894     pos = co_insert_pos(pt, co1, 0, pt_len_get(pt));
895     /* check if exists */
896     if (pos < pt_len_get(pt)){
897 	co2 = pt_vec_i_get(pt, pos); /* insert after co2 */
898 	if (co1 == NULL && co2==NULL)
899 	    return NULL;
900 	if (co1 && co2 && co_eq(co1, co2)==0){
901 	    cligen_parsetree_merge(co_pt_get(co2), co2, co_pt_get(co1));
902 	    co_free(co1, 1);
903 	    return co2;
904 	}
905     }
906     if (pt_vec_i_insert(pt, pos, co1) < 0)
907 	return NULL;
908     return co1;
909 }
910 
911 /*! Given a parse tree, find the first CLIgen object that matches
912  * @param[in]  pt      CLIgen parse-tree
913  * @param[in]  name    Name of node
914  * @retval     co      Object found
915  * @retval     NULL    Not found
916  * You can only use this if the child-list is alphabetically
917  * sorted. You get this automatically with co_insert(), bit some code
918  * may add children w/o co_insert.
919  * No recursion
920  */
921 cg_obj *
co_find_one(parse_tree * pt,char * name)922 co_find_one(parse_tree *pt,
923 	    char       *name)
924 {
925     return co_search1(pt, name, 0, pt_len_get(pt));
926 }
927 
928 /*! Set CLIgen object value
929  * Allocate new string, remove old if already set.
930  * @param[in]  co      CLIgen object
931  * @param[in]  str     Value to set
932  * @retval     0       OK
933  * @retval     -1      Error
934  */
935 int
co_value_set(cg_obj * co,char * str)936 co_value_set(cg_obj *co,
937 	     char   *str)
938 {
939     if (co->co_value){ /* This can happen in '?/TAB' since we call match twice */
940 	free(co->co_value);
941 	co->co_value = NULL;
942     }
943     if (str != NULL)
944 	if ((co->co_value = strdup(str)) == NULL){
945 	    fprintf(stderr, "%s: strdup: %s\n", __FUNCTION__, strerror(errno));
946 	    return -1;
947 	}
948     return 0;
949 }
950 
951 /*! Create (malloc) a reason string using var-list args
952  * General purpose routine but used for error handling.
953  * @param[in]  fmt     Format string followed by a variable list (as in  printf)
954  * @retval     reason  Formatted reason string. Free with free()
955  * @retval     NULL    Error
956  */
957 char *
cligen_reason(const char * fmt,...)958 cligen_reason(const char *fmt, ...)
959 {
960     char   *reason;
961     int     res;
962     int     len;
963     va_list ap;
964 
965     va_start(ap, fmt);
966     len = vsnprintf(NULL, 0, fmt, ap);
967     len++;
968     va_end(ap);
969     if ((reason = malloc(len)) == NULL)
970 	return NULL;
971     va_start(ap, fmt);
972     if ((res = vsnprintf(reason, len, fmt, ap)) < 0){
973 	free(reason);
974 	reason = NULL;
975     }
976     va_end(ap);
977     return reason;
978 }
979