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