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