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   CLIgen variable vectors - cvec
35 */
36 
37 #include "cligen_config.h"
38 
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <stdint.h>
42 #include <inttypes.h>
43 #include <unistd.h>
44 #include <sys/types.h>
45 #include <time.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49 #include <string.h>
50 #include <errno.h>
51 
52 #include "cligen_buf.h"
53 #include "cligen_cv.h"
54 #include "cligen_cvec.h"
55 #include "cligen_parsetree.h"
56 #include "cligen_object.h"
57 #include "cligen_io.h"
58 #include "cligen_match.h"
59 #include "cligen_getline.h"
60 
61 #include "cligen_cv_internal.h"
62 #include "cligen_cvec_internal.h"
63 
64 /*! A malloc version that aligns on 4 bytes. To avoid warning from valgrind */
65 #define align4(s) (((s)/4)*4 + 4)
66 
67 /*! A strdup version that aligns on 4 bytes. To avoid warning from valgrind */
strdup4(char * str)68 static inline char * strdup4(char *str)
69 {
70     char *dup;
71     int len;
72 
73     len = align4(strlen(str)+1);
74     if ((dup = malloc(len)) == NULL)
75 	return NULL;
76     strcpy(dup, str);
77     return dup;
78 }
79 
80 /*
81  * cv_exclude_keys
82  * set if you want to backward compliant: dont include keys in cgv vec to callback
83  * that is, regular 'keys' and keys like: '<string keyword=foo>'
84  */
85 static int excludekeys = 0;
86 
87 /*! Create and initialize a new cligen variable vector (cvec)
88  *
89  * Each individual cv initialized with CGV_ERR and no value.
90  * Returned cvec needs to be freed with cvec_free().
91  *
92  * @param[in] len    Number of cv elements. Can be zero and elements added incrementally.
93  * @retval    NULL   errno set
94  * @retval    cvv    allocated cligen var vector
95  * @see cvec_init
96  */
97 cvec *
cvec_new(int len)98 cvec_new(int len)
99 {
100     cvec *cvv;
101 
102     if ((cvv = malloc(sizeof(*cvv))) == NULL)
103 	return NULL;
104     memset(cvv, 0, sizeof(*cvv));
105     if (cvec_init(cvv, len) < 0){
106 	free(cvv);
107 	return NULL;
108     }
109     return cvv;
110 }
111 
112 /*! Create a new vector, initialize the first element to the contents of 'var'
113  *
114  * @param[in] var      cg_var to clone and add to vector
115  * @retval    cvec     allocated cvec
116  */
117 cvec *
cvec_from_var(cg_var * cv)118 cvec_from_var(cg_var *cv)
119 {
120     cvec   *newvec = NULL;
121     cg_var *tail = NULL;
122 
123     if (cv && (newvec = cvec_new(0))) {
124         if ((tail = cvec_append_var(newvec, cv)) == NULL) {
125             cvec_free(newvec);
126             newvec = NULL;
127         }
128     }
129     return newvec;
130 }
131 
132 /*! Free a cligen  variable vector (cvec)
133  *
134  * Reset and free a cligen vector as previously created by cvec_new(). this includes
135  * freeing all cv:s that the cvec consists of.
136  * @param[in]  cvv   Cligen variable vector
137  * @see cvec_new
138  */
139 int
cvec_free(cvec * cvv)140 cvec_free(cvec *cvv)
141 {
142     if (cvv) {
143 	cvec_reset(cvv);
144 	free(cvv);
145     }
146     return 0;
147 }
148 
149 /*! Initialize a cligen variable vector (cvec) with 'len' numbers of variables.
150  *
151  * Each individual cv initialized with CGV_ERR and no value.
152  *
153  * @param[in] cvv  Cligen variable vector
154  * @param[in] len  Number of cv elements. Can be zero and elements added incrementally.
155  * @see cvec_new
156  */
157 int
cvec_init(cvec * cvv,int len)158 cvec_init(cvec *cvv,
159 	  int   len)
160 {
161     cvv->vr_len = len;
162     if (len && (cvv->vr_vec = calloc(cvv->vr_len, sizeof(cg_var))) == NULL)
163 	return -1;
164     return 0;
165 }
166 
167 /*! Reset cligen variable vector resetting it to an initial state as returned by cvec_new
168  *
169  * @param[in]  cvv   Cligen variable vector
170  * @see also cvec_free. But this function does not actually free the cvec.
171  */
172 int
cvec_reset(cvec * cvv)173 cvec_reset(cvec *cvv)
174 {
175     cg_var *cv = NULL;
176 
177     if (cvv == NULL) {
178 	return 0;
179     }
180 
181     while ((cv = cvec_each(cvv, cv)) != NULL)
182 	cv_reset(cv);
183     if (cvv->vr_vec)
184 	free(cvv->vr_vec);
185     if (cvv->vr_name)
186 	free(cvv->vr_name);
187     memset(cvv, 0, sizeof(*cvv));
188     return 0;
189 }
190 
191 /*! Given a cv in a cligen variable vector (cvec) return the next cv.
192  *
193  * @param[in]  cvv    The cligen variable vector
194  * @param[in]  cv0    Return element after this, or first element if this is NULL
195  * Given an element (cv0) in a cligen variable vector (cvec) return the next element.
196  * @retval cv  Next element
197  */
198 cg_var *
cvec_next(cvec * cvv,cg_var * cv0)199 cvec_next(cvec   *cvv,
200 	  cg_var *cv0)
201 {
202     cg_var *cv = NULL;
203     int i;
204 
205     if (!cvv) {
206 	    return 0;
207     }
208 
209     if (cv0 == NULL)
210 	cv = cvv->vr_vec;
211     else {
212 	i = cv0 - cvv->vr_vec;
213 	if (i < cvv->vr_len-1)
214 	    cv = cv0 + 1;
215     }
216     return cv;
217 }
218 
219 /*! Append a new cligen variable (cv) to cligen variable vector (cvec) and return it.
220  *
221  * @param[in] cvv   Cligen variable vector
222  * @param[in] type  Append a new cv to the vector with this type
223  * @retval    NULL  Error
224  * @retval    cv    The new cligen variable
225  * @see also cv_new, but this is allocated contiguosly as a part of a cvec.
226  */
227 cg_var *
cvec_add(cvec * cvv,enum cv_type type)228 cvec_add(cvec        *cvv,
229 	 enum cv_type type)
230 {
231     int     len;
232     cg_var *cv;
233 
234     if (cvv == NULL){
235 	errno = EINVAL;
236 	return NULL;
237     }
238     len = cvv->vr_len + 1;
239 
240     if ((cvv->vr_vec = realloc(cvv->vr_vec, len*sizeof(cg_var))) == NULL)
241 	return NULL;
242     cvv->vr_len = len;
243     cv = cvec_i(cvv, len-1);
244     memset(cv, 0, sizeof(*cv));
245     cv->var_type = type;
246     return cv;
247 }
248 
249 /*! Append a new var that is a clone of data in 'cv' to the vector, return it
250  * @param[in] cvv  Cligen variable vector
251  * @param[in] cv   Append this cligen variable to vector. Note that it is copied.
252  * @retval    NULL Error
253  * @retval    tail Return the new last tail variable (copy of cv)
254  */
255 cg_var *
cvec_append_var(cvec * cvv,cg_var * cv)256 cvec_append_var(cvec   *cvv,
257 		cg_var *cv)
258 {
259     cg_var *tail = NULL;
260 
261     if (cvv && cv && (tail = cvec_add(cvv, cv_type_get(cv)))) {
262         if (cv_cp(tail, cv) < 0) {
263             cvec_del(cvv, tail);
264             tail = NULL;
265         }
266     }
267     return tail;
268 }
269 
270 /*! Delete a cv variable from a cvec. Note: cv is not reset & cv may be stale!
271  *
272  * @param[in]  cvv   Cligen variable vector
273  * @param[in]  del   variable to delete
274  *
275  * @note This is a dangerous command since the cv it deletes (such as created by
276  * cvec_add) may have been modified with realloc (eg cvec_add/delete) and
277  * therefore can not be used as a reference.  Safer methods are to use
278  * cvec_find/cvec_i to find a cv and then to immediately remove it.
279  */
280 int
cvec_del(cvec * cvv,cg_var * del)281 cvec_del(cvec   *cvv,
282 	 cg_var *del)
283 {
284     int i;
285     cg_var *cv;
286 
287     if (cvec_len(cvv) == 0)
288 	return 0;
289 
290     i = 0;
291     cv = NULL;
292     while ((cv = cvec_each(cvv, cv)) != NULL) {
293 	if (cv == del)
294 	    break;
295 	i++;
296     }
297     if (i >= cvec_len(cvv)) /* Not found !?! */
298 	return cvec_len(cvv);
299 
300     if (i != cvec_len(cvv)-1) /* If not last entry, move the remaining cv's */
301 	memmove(&cvv->vr_vec[i], &cvv->vr_vec[i+1],
302 		(cvv->vr_len-i-1) * sizeof(cvv->vr_vec[0]));
303 
304     cvv->vr_len--;
305     cvv->vr_vec = realloc(cvv->vr_vec, cvv->vr_len*sizeof(cvv->vr_vec[0])); /* Shrink should not fail? */
306 
307     return cvec_len(cvv);
308 }
309 
310 /*! Delete a cv variable from a cvec using index i
311  *
312  * @param[in]  cvv   Cligen variable vector
313  * @param[in]  del   variable to delete
314  *
315  * @note This is a dangerous command since the cv it deletes (such as created by
316  * cvec_add) may have been modified with realloc (eg cvec_add/delete) and
317  * therefore can not be used as a reference.  Safer methods are to use
318  * cvec_find/cvec_i to find a cv and then to immediately remove it.
319  * @note does not deallocate the cv, you may need to do it with cv_reset
320  */
321 int
cvec_del_i(cvec * cvv,int i)322 cvec_del_i(cvec *cvv,
323 	   int   i)
324 {
325     if (cvec_len(cvv) == 0 || cvec_len(cvv) < i)
326 	return 0;
327 
328     if (i != cvec_len(cvv)-1) /* If not last entry, move the remaining cv's */
329 	memmove(&cvv->vr_vec[i], &cvv->vr_vec[i+1],
330 		(cvv->vr_len-i-1) * sizeof(cvv->vr_vec[0]));
331 
332     cvv->vr_len--;
333 
334     return cvec_len(cvv);
335 }
336 
337 /*! Return allocated length of a cvec.
338  * @param[in]  cvv   Cligen variable vector
339  */
340 int
cvec_len(cvec * cvv)341 cvec_len(cvec *cvv)
342 {
343 	if (!cvv) {
344 		return 0;
345 	}
346     return cvv->vr_len;
347 }
348 
349 /*! Return i:th element of cligen variable vector cvec.
350  * @param[in]  cvv   Cligen variable vector
351  * @param[in]  i     Order of element to get
352  */
353 cg_var *
cvec_i(cvec * cvv,int i)354 cvec_i(cvec *cvv,
355        int   i)
356 {
357 	if (!cvv) {
358 		return NULL;
359 	}
360     if (i < cvv->vr_len)
361 	return &cvv->vr_vec[i];
362     return NULL;
363 }
364 
365 /*! Return string value of i:th element of cligen variable vector. Helper function.
366  * @param[in]  cvv   Cligen variable vector
367  * @param[in]  i     Order of element to get
368  * @retval     str   String value of i:th element
369  * @retval     NULL  Element does not exist
370  */
371 char *
cvec_i_str(cvec * cvv,int i)372 cvec_i_str(cvec *cvv,
373 	   int   i)
374 {
375     cg_var *cv;
376 
377     if ((cv = cvec_i(cvv, i)) == NULL)
378 	return NULL;
379     return cv_string_get(cv);
380 }
381 
382 /*! Iterate through all cligen variables in a cvec list
383  *
384  * @param[in] cvv       Cligen variable vector
385  * @param[in] prev	Last cgv (or NULL)
386  * @retval cv           Next variable structure.
387  * @retval NULL         When end of list reached.
388  * @code
389  *    cg_var *cv = NULL;
390  *    while ((cv = cvec_each(cvv, cv)) != NULL)
391  *	     ...
392  * @endcode
393  */
394 cg_var *
cvec_each(cvec * cvv,cg_var * prev)395 cvec_each(cvec   *cvv,
396 	  cg_var *prev)
397 {
398 	if (!cvv) {
399 		return 0;
400 	}
401 
402   if (prev == NULL){   /* Initialization */
403       if (cvv->vr_len > 0)
404 	  return &cvv->vr_vec[0];
405       else
406 	  return NULL;
407   }
408   return cvec_next(cvv, prev);
409 }
410 
411 /*! Iterate through all except first cligen variables in a cvec list
412  *
413  * @param[in] cvv   Cligen variable vector
414  * @param[in] prev  Last cgv (or NULL)
415  * @retval cv       Next variable structure.
416  * @retval NULL     When end of list reached.
417  * Common in many cvecs where [0] is the command-line and all
418  * others are arguments.
419  * @see cvec_each  For all elements, dont skip first
420  */
421 cg_var *
cvec_each1(cvec * cvv,cg_var * prev)422 cvec_each1(cvec   *cvv,
423 	   cg_var *prev)
424 {
425     if (!cvv) {
426 	    return 0;
427     }
428 
429   if (prev == NULL){   /* Initialization */
430       if (cvv->vr_len > 1)
431 	  return &cvv->vr_vec[1];
432       else
433 	  return NULL;
434   }
435   return cvec_next(cvv, prev);
436 }
437 
438 /*! Create a new cvec by copying from an original
439  *
440  * @param[in]   old   The cvec to copy from
441  * @retval      new   The cvec copy. Free this with cvec_free
442  * @retval      NULL  Error
443  * The new cvec needs to be freed by cvec_free().
444  * One can make a cvec_cp() as well but it is a little trickier to match vr_vec.
445  */
446 cvec *
cvec_dup(cvec * old)447 cvec_dup(cvec *old)
448 {
449     cvec   *new;
450     cg_var *cv0 = NULL;
451     cg_var *cv1;
452     int     i;
453 
454     if (!old) {
455 	    return NULL;
456     }
457 
458     if ((new = cvec_new(old->vr_len)) == NULL)
459 	return NULL;
460     if (old->vr_name)
461 	if ((new->vr_name = strdup4(old->vr_name)) == NULL)
462 	    return NULL;
463     i = 0;
464     while ((cv0 = cvec_each(old, cv0)) != NULL) {
465 	cv1 = cvec_i(new, i++);
466 	cv_cp(cv1, cv0);
467     }
468     return new;
469 }
470 
471 /*! Create a cv list with a single string element.
472  *
473  * @param[in]  cmd  Text string
474  * @retval     NULL Error
475  * @retval     cvv  Cligen variable list
476  * Help function when creating cvec to cligen callbacks.
477  */
478 cvec *
cvec_start(char * cmd)479 cvec_start(char *cmd)
480 {
481     cvec *cvec;
482     cg_var    *cv;
483 
484     if ((cvec = cvec_new(1)) == NULL){
485 	fprintf(stderr, "%s: cvec_new: %s\n", __FUNCTION__, strerror(errno));
486 	return NULL;
487     }
488     cv = cvec_i(cvec, 0);
489     cv->var_type = CGV_REST;
490     cv_name_set(cv, "cmd"); /* the whole command string */
491     cv_string_set(cv, cmd); /* the whole command string */
492     return cvec;
493 }
494 
495 /*! Pretty print cligen variable list to a file
496  * @param[in]  f    File to print to
497  * @param[in]  cvv  Cligen variable vector to print
498  * @see cvec2cbuf
499  */
500 int
cvec_print(FILE * f,cvec * cvv)501 cvec_print(FILE *f,
502 	   cvec *cvv)
503 {
504     cg_var *cv = NULL;
505     char   *name;
506     int     i = 0;
507 
508     if ((name = cvec_name_get(cvv)) != NULL)
509 	fprintf(f, "%s:\n", name);
510     while ((cv = cvec_each(cvv, cv)) != NULL) {
511 	name = cv_name_get(cv);
512 	if (name)
513 	    fprintf(f, "%d : %s = ", i++, name);
514 	else
515 	    fprintf(f, "%d : ", i++);
516 	cv_print(f, cv);
517 	fprintf(f, "\n");
518     }
519     return 0;
520 }
521 
522 /*! Pretty print cligen variable list to a cligen buffer
523  * @param[out] cb   Cligen buffer (should already be initialized w cbuf_new)
524  * @param[in]  cvv  Cligen variable vector to print
525  * @see cvec_print
526  */
527 int
cvec2cbuf(cbuf * cb,cvec * cvv)528 cvec2cbuf(cbuf *cb,
529 	  cvec *cvv)
530 {
531     cg_var *cv = NULL;
532     int     i = 0;
533     char   *s;
534 
535     while ((cv = cvec_each(cvv, cv)) != NULL) {
536 	if ((s = cv2str_dup(cv)) == NULL)
537 	    return -1;
538 	cprintf(cb, "%d : %s = %s\n", i++, cv_name_get(cv), s);
539 	free(s);
540     }
541     return 0;
542 }
543 
544 /*! Return first cv in a cvec matching a name
545  *
546  * Given an CLIgen variable vector cvec, and the name of a variable, return the
547  * first matching entry.
548  * @param[in]  cvv   Cligen variable vector
549  * @param[in]  name  Name to match (can be NULL)
550  * @retval     cv    Element matching name. NULL
551  * @retval     NULL  Not found
552  * @see cvec_find_keyword
553  */
554 cg_var *
cvec_find(cvec * cvv,char * name)555 cvec_find(cvec *cvv,
556 	  char *name)
557 {
558     cg_var *cv = NULL;
559 
560     while ((cv = cvec_each(cvv, cv)) != NULL){
561 	if (cv->var_name){
562 	    if (name != NULL && strcmp(cv->var_name, name) == 0)
563 		return cv;
564 	}
565 	else if (name == NULL)
566 	    return cv;
567     }
568     return NULL;
569 }
570 
571 /*! Return first keyword cv in a cvec matching a name
572  * @param[in]  cvv   Cligen variable vector
573  * @param[in]  name  Name to match
574  * @retval     cv    Element matching name. NULL
575  * @retval     NULL  Not found
576  * @see cvec_find
577  */
578 cg_var *
cvec_find_keyword(cvec * cvv,char * name)579 cvec_find_keyword(cvec *cvv,
580 		  char *name)
581 {
582     cg_var *cv = NULL;
583 
584     while ((cv = cvec_each(cvv, cv)) != NULL)
585 	if (cv->var_name && strcmp(cv->var_name, name) == 0 && cv->var_const)
586 	    return cv;
587     return NULL;
588 }
589 
590 /*! Return first non-keyword cv in a cvec matching a name
591  * @param[in]  cvv   Cligen variable vector
592  * @param[in]  name  Name to match
593  * @retval     cv    Element matching name. NULL
594  * @retval     NULL  Not found
595  * @see cvec_find
596  */
597 cg_var *
cvec_find_var(cvec * cvv,char * name)598 cvec_find_var(cvec *cvv,
599 	      char *name)
600 {
601     cg_var *cv = NULL;
602 
603     while ((cv = cvec_each(cvv, cv)) != NULL)
604 	if (cv->var_name && strcmp(cv->var_name, name) == 0 && !cv->var_const)
605 	    return cv;
606     return NULL;
607 }
608 
609 /*! Typed version of cvec_find that returns the string value.
610  *
611 
612  * @param[in]  cvv   Cligen variable vector
613  * @param[in]  name  Name to match
614  * @retval     cv    Element matching name. NULL
615  * @retval     NULL  Not found
616  * @note Does not see the difference between not finding the cv, and finding one
617  *           with wrong type - in both cases NULL is returned.
618  * @note The returned string must be copied since it points directly into the cv.
619  * @see cvec_find
620  */
621 char *
cvec_find_str(cvec * cvv,char * name)622 cvec_find_str(cvec *cvv,
623 	      char *name)
624 {
625     cg_var *cv;
626 
627     if ((cv = cvec_find(cvv, name)) != NULL && cv_isstring(cv->var_type))
628 	return cv_string_get(cv);
629     return NULL;
630 }
631 
632 /*! Get name of cligen variable vector
633  * @param[in]  cvv  Cligen variable vector
634  * @retval str  The name of the cvec as a string, can be NULL, no copy
635  * @retval      name Name of variable vector
636  */
637 char *
cvec_name_get(cvec * cvv)638 cvec_name_get(cvec *cvv)
639 {
640     return cvv->vr_name;
641 }
642 
643 /*! Allocate and set name of cligen variable vector, including NULL
644  * @param[in]  cvv    A cligen variable vector
645  * @param[in]  name   A string that is copied and used as a cvec name, or NULL
646  * @retval     str    The name of the cvec.
647  * The existing name, if any, is freed
648  */
649 char *
cvec_name_set(cvec * cvv,char * name)650 cvec_name_set(cvec *cvv,
651 	      char *name)
652 {
653     char *s1 = NULL;
654 
655     /* Duplicate name. Must be done before a free, in case name is part of the original */
656     if (name){
657 	if ((s1 = strdup4(name)) == NULL)
658 	    return NULL; /* error in errno */
659     }
660     if (cvv->vr_name != NULL)
661 	free(cvv->vr_name);
662     cvv->vr_name = s1;
663     return s1;
664 }
665 
666 
667 /*! Changes cvec find function behaviour, exclude keywords or include them.
668  * @param[in] status
669  */
670 int
cv_exclude_keys(int status)671 cv_exclude_keys(int status)
672 {
673     excludekeys = status;
674     return 0;
675 }
676 /*! Changes cvec find function behaviour, exclude keywords or include them.
677  * @param[in] status
678  */
679 int
cv_exclude_keys_get(void)680 cv_exclude_keys_get(void)
681 {
682     return excludekeys;
683 }
684 
685 /*! Return the alloced memory of a CLIgen variable vector
686  */
687 size_t
cvec_size(cvec * cvv)688 cvec_size(cvec *cvv)
689 {
690     size_t  sz = 0;
691     cg_var *cv = NULL;
692 
693     sz += sizeof(struct cvec);
694     if (cvv->vr_name)
695 	sz += strlen(cvv->vr_name)+1;
696     cv = NULL;
697     while ((cv = cvec_each(cvv, cv)) != NULL)
698 	sz += cv_size(cv);
699     return sz;
700 }
701