1 /*
2 CLI generator. Take idl as input and generate a tree for use in cli.
3
4 ***** BEGIN LICENSE BLOCK *****
5
6 Copyright (C) 2001-2020 Olof Hagsand
7
8 This file is part of CLIgen.
9
10 Licensed under the Apache License, Version 2.0 (the "License");
11 you may not use this file except in compliance with the License.
12 You may obtain a copy of the License at
13
14 http://www.apache.org/licenses/LICENSE-2.0
15
16 Unless required by applicable law or agreed to in writing, software
17 distributed under the License is distributed on an "AS IS" BASIS,
18 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 See the License for the specific language governing permissions and
20 limitations under the License.
21
22 Alternatively, the contents of this file may be used under the terms of
23 the GNU General Public License Version 2 or later (the "GPL"),
24 in which case the provisions of the GPL are applicable instead
25 of those above. If you wish to allow use of your version of this file only
26 under the terms of the GPL, and not to allow others to
27 use your version of this file under the terms of Apache License version 2, indicate
28 your decision by deleting the provisions above and replace them with the
29 notice and other provisions required by the GPL. If you do not delete
30 the provisions above, a recipient may use your version of this file under
31 the terms of any one of the Apache License version 2 or the GPL.
32
33 ***** END LICENSE BLOCK *****
34
35
36 Choice:
37 */
38 %start file
39
40 %union {
41 int intval;
42 char *string;
43 }
44
45 %token MY_EOF
46 %token V_RANGE V_LENGTH V_CHOICE V_KEYWORD V_REGEXP V_FRACTION_DIGITS V_SHOW V_TREENAME V_TRANSLATE
47 %token DOUBLEPARENT /* (( */
48 %token DQ /* " */
49 %token DQP /* ") */
50 %token PDQ /* (" */
51 %token SETS /* @{ SETS */
52
53 %token <string> NAME /* in variables: <NAME type:NAME> */
54 %token <string> NUMBER /* In variables */
55 %token <string> DECIMAL /* In variables */
56 %token <string> CHARS
57
58 %type <string> charseq
59 %type <string> choices
60 %type <string> numdec
61 %type <string> arg1
62 %type <string> typecast
63 %type <intval> preline
64
65 %lex-param {void *_cy} /* Add this argument to parse() and lex() function */
66 %parse-param {void *_cy}
67
68 %{
69 /* Here starts user C-code */
70
71 /* typecast macro */
72 #define _CY ((cligen_yacc *)_cy)
73
74 /* add _cy to error paramaters */
75 #define YY_(msgid) msgid
76
77 #include "cligen_config.h"
78
79 #include <stdio.h>
80 #include <string.h>
81 #include <stdint.h>
82 #include <stdlib.h>
83 #include <errno.h>
84 #include <limits.h>
85 #include <sys/types.h>
86 #include <sys/socket.h>
87 #include <netinet/in.h>
88 #include <net/if.h>
89
90 #include "cligen_buf.h"
91 #include "cligen_cv.h"
92 #include "cligen_cvec.h"
93 #include "cligen_parsetree.h"
94 #include "cligen_pt_head.h"
95 #include "cligen_object.h"
96 #include "cligen_match.h"
97 #include "cligen_syntax.h"
98 #include "cligen_handle.h"
99 #include "cligen_parse.h"
100
101 static int debug = 0;
102
103 extern int cligen_parseget_lineno (void);
104
105 int
cligen_parse_debug(int d)106 cligen_parse_debug(int d)
107 {
108 debug = d;
109 return 0;
110 }
111
112 /*! CLIGEN parse error routine
113 * Also called from yacc generated code *
114 * @param[in] cy CLIgen yacc parse struct
115 */
cligen_parseerror(void * _cy,char * s)116 void cligen_parseerror(void *_cy,
117 char *s)
118 {
119 fprintf(stderr, "%s%s%d: Error: %s: at or before: '%s'\n",
120 _CY->cy_name,
121 ":" ,
122 _CY->cy_linenum ,
123 s,
124 cligen_parsetext);
125 return;
126 }
127
128 #define cligen_parseerror1(cy, s) cligen_parseerror(cy, s)
129
130 /*! Create a CLIgen variable (cv) and store it in the current variable object
131 * Note that only one such cv can be stored.
132 * @param[in] cy CLIgen yacc parse struct
133 */
134 static cg_var *
create_cv(cligen_yacc * cy,char * type,char * str)135 create_cv(cligen_yacc *cy,
136 char *type,
137 char *str)
138 {
139 cg_var *cv = NULL;
140
141 if ((cv = cv_new(CGV_STRING)) == NULL){
142 fprintf(stderr, "malloc: %s\n", strerror(errno));
143 goto done;
144 }
145 if (type){
146 if (cv_type_set(cv, cv_str2type(type)) == CGV_ERR){
147 fprintf(stderr, "%s:%d: error: No such type: %s\n",
148 cy->cy_name, cy->cy_linenum, type);
149 cv_free(cv); cv = NULL;
150 goto done;
151 }
152 }
153 if (cv_parse(str, cv) < 0){ /* parse str into cgv */
154 cv_free(cv); cv = NULL;
155 goto done;
156 }
157 done:
158 return cv;
159 }
160
161 /*!
162 * @param[in] cy CLIgen yacc parse struct
163 */
164 static int
cgy_flag(cligen_yacc * cy,char * var)165 cgy_flag(cligen_yacc *cy,
166 char *var)
167 {
168 struct cgy_stack *cs = cy->cy_stack;
169 cg_var *cv;
170 int retval = -1;
171
172 if (debug)
173 fprintf(stderr, "%s: %s 1\n", __FUNCTION__, var);
174 if (cs){ /* XXX: why cs? */
175 if (cy->cy_cvec == NULL){
176 if ((cy->cy_cvec = cvec_new(0)) == NULL){
177 fprintf(stderr, "%s: cvec_new:%s\n", __FUNCTION__, strerror(errno));
178 goto done;
179 }
180 }
181 if ((cv = cvec_add(cy->cy_cvec, CGV_INT32)) == NULL){
182 fprintf(stderr, "%s: realloc:%s\n", __FUNCTION__, strerror(errno));
183 goto done;
184 }
185 cv_name_set(cv, var);
186 cv_int32_set(cv, 1);
187 }
188 retval = 0;
189 done:
190 return retval;
191 }
192
193 /*! Set a new treename. In fact registers the previous tree and creates a new .
194 * Note that one could have used an assignment: treename = <name>; for this but
195 * I decided to create special syntax for this so that assignments can use any
196 * variable names.
197 * @param[in] cy CLIgen yacc parse struct
198 * @param[in] name Name of tree
199 */
200 static int
cgy_treename(cligen_yacc * cy,char * name)201 cgy_treename(cligen_yacc *cy,
202 char *name)
203 {
204 cg_obj *co = NULL;
205 cg_obj *cot;
206 struct cgy_list *cl;
207 int retval = -1;
208 int i;
209 parse_tree *pt;
210 pt_head *ph;
211
212 /* Get the first object */
213 for (cl=cy->cy_list; cl; cl = cl->cl_next){
214 co = cl->cl_obj;
215 break;
216 }
217 /* Get the top object */
218 cot = co_top(co); /* co and cot can be same object */
219 pt = co_pt_get(cot);
220 /* If anything parsed */
221 if (pt_len_get(pt)){
222 /* 2. Add the old parse-tree with old name*/
223 for (i=0; i<pt_len_get(pt); i++){
224 if ((co=pt_vec_i_get(pt, i)) != NULL)
225 co_up_set(co, NULL);
226 }
227 if ((ph = cligen_ph_add(cy->cy_handle, cy->cy_treename)) == NULL)
228 goto done;
229 if (cligen_ph_parsetree_set(ph, pt) < 0)
230 goto done;
231 /* 3. Create new parse-tree XXX */
232 if ((pt = pt_new()) == NULL)
233 goto done;
234 co_pt_clear(cot);
235 co_pt_set(cot, pt);
236 }
237
238 /* 4. Set the new name */
239 if (cy->cy_treename)
240 free(cy->cy_treename);
241 if ((cy->cy_treename = strdup(name)) == NULL){
242 fprintf(stderr, "%s: strdup: %s\n", __FUNCTION__, strerror(errno));
243 goto done;
244 }
245 retval = 0;
246 done:
247 return retval;
248 }
249
250 /*! Variable assignment
251 * Only string type supported for now
252 * @param[in] cy CLIgen yacc parse struct
253 */
254 static int
cgy_assignment(cligen_yacc * cy,char * var,char * val)255 cgy_assignment(cligen_yacc *cy,
256 char *var,
257 char *val)
258 {
259 struct cgy_stack *cs = cy->cy_stack;
260 int retval = -1;
261 cg_var *cv;
262 char *treename_keyword;
263 cligen_handle h = cy->cy_handle;
264
265 if (debug)
266 fprintf(stderr, "%s: %s=%s\n", __FUNCTION__, var, val);
267 if (cs == NULL){
268 fprintf(stderr, "%s: Error, stack should not be NULL\n", __FUNCTION__);
269 }
270 if (cs->cs_next != NULL){ /* local */
271 if (cy->cy_cvec == NULL)
272 if ((cy->cy_cvec = cvec_new(0)) == NULL){
273 fprintf(stderr, "%s: cvec_new:%s\n", __FUNCTION__, strerror(errno));
274 goto done;
275 }
276 if ((cv = cvec_add(cy->cy_cvec, CGV_STRING)) == NULL){
277 fprintf(stderr, "%s: realloc:%s\n", __FUNCTION__, strerror(errno));
278 goto done;
279 }
280 cv_name_set(cv, var);
281 if (cv_parse(val, cv) < 0)
282 goto done;
283 }
284 else{ /* global */
285 treename_keyword = cligen_treename_keyword(h); /* typically: "treename" */
286 if (strcmp(var, treename_keyword) == 0){
287 if (cgy_treename(cy, val) < 0)
288 goto done;
289 }
290 else {
291 if ((cv = cvec_add(cy->cy_globals, CGV_STRING)) == NULL){
292 fprintf(stderr, "%s: realloc:%s\n", __FUNCTION__, strerror(errno));
293 goto done;
294 }
295 cv_name_set(cv, var);
296 if (cv_parse(val, cv) < 0) /* May be wrong type */
297 goto done;
298 }
299 }
300 retval = 0;
301 done:
302 return retval;
303 }
304
305 /*!
306 * @param[in] cy CLIgen yacc parse struct
307 */
308 int
cgy_callback(cligen_yacc * cy,char * cb_str)309 cgy_callback(cligen_yacc *cy,
310 char *cb_str)
311 {
312 struct cgy_stack *cs = cy->cy_stack;
313 struct cg_callback *cc, **ccp;
314
315 if (debug)
316 fprintf(stderr, "%s: %s\n", __FUNCTION__, cb_str);
317 if (cs == NULL)
318 return 0;
319 ccp = &cy->cy_callbacks;
320 while (*ccp != NULL)
321 ccp = &((*ccp)->cc_next);
322 if ((cc = malloc(sizeof(*cc))) == NULL){
323 fprintf(stderr, "%s: malloc: %s\n", __FUNCTION__, strerror(errno));
324 cligen_parseerror1(cy, "Allocating cligen callback");
325 return -1;
326 }
327 memset(cc, 0, sizeof(*cc));
328 cc->cc_fn_str = cb_str;
329 *ccp = cc;
330 return 0;
331 }
332
333 /*! Create a callback argument and store it in the current callback
334 * @param[in] cy CLIgen yacc parse struct
335 */
336 static int
cgy_callback_arg(cligen_yacc * cy,char * type,char * arg)337 cgy_callback_arg(cligen_yacc *cy,
338 char *type,
339 char *arg)
340 {
341 int retval = -1;
342 struct cg_callback *cc;
343 struct cg_callback *cclast;
344 cg_var *cv = NULL;
345
346 cclast = NULL;
347 for (cc=cy->cy_callbacks; cc; cc=cc->cc_next)
348 cclast = cc;
349 if (cclast){
350 if ((cv = create_cv(cy, type, arg)) == NULL)
351 goto done;
352 if (cclast->cc_cvec)
353 cvec_append_var(cclast->cc_cvec, cv);
354 else
355 cclast->cc_cvec = cvec_from_var(cv);
356 }
357 retval = 0;
358 done:
359 if (cv)
360 cv_free(cv);
361 return retval;
362 }
363
364 /*!
365 * @param[in] cy CLIgen yacc parse struct
366 */
367 static int
expand_arg(cligen_yacc * cy,char * type,char * arg)368 expand_arg(cligen_yacc *cy,
369 char *type,
370 char *arg)
371 {
372 int retval = -1;
373 cg_var *cv = NULL;
374
375 if ((cv = create_cv(cy, type, arg)) == NULL)
376 goto done;
377 if (cy->cy_var->co_expand_fn_vec)
378 cvec_append_var(cy->cy_var->co_expand_fn_vec, cv);
379 else
380 cy->cy_var->co_expand_fn_vec = cvec_from_var(cv);
381 retval = 0;
382 done:
383 if (cv)
384 cv_free(cv);
385 return retval;
386 }
387
388 /*!
389 * @param[in] cy CLIgen yacc parse struct
390 */
391 static int
expand_fn(cligen_yacc * cy,char * fn)392 expand_fn(cligen_yacc *cy,
393 char *fn)
394 {
395 cy->cy_var->co_expand_fn_str = fn;
396 return 0;
397 }
398
399 static int
cg_translate(cligen_yacc * cy,char * fn)400 cg_translate(cligen_yacc *cy,
401 char *fn)
402 {
403 cy->cy_var->co_translate_fn_str = fn;
404 return 0;
405 }
406
407 static int
cgy_list_push(cg_obj * co,struct cgy_list ** cl0)408 cgy_list_push(cg_obj *co,
409 struct cgy_list **cl0)
410 {
411 struct cgy_list *cl;
412
413 if (debug)
414 fprintf(stderr, "%s\n", __FUNCTION__);
415 if ((cl = malloc(sizeof(*cl))) == NULL) {
416 fprintf(stderr, "%s: malloc: %s\n", __FUNCTION__, strerror(errno));
417 return -1;
418 }
419 cl->cl_next = *cl0;
420 cl->cl_obj = co;
421 *cl0 = cl;
422 return 0;
423 }
424
425 /*! Delete whole list
426 * @param[in] cy CLIgen yacc parse struct
427 */
428 static int
cgy_list_delete(struct cgy_list ** cl0)429 cgy_list_delete(struct cgy_list **cl0)
430 {
431 struct cgy_list *cl;
432
433 while ((cl = *cl0) != NULL){
434 *cl0 = cl->cl_next;
435 free(cl);
436 }
437 return 0;
438 }
439
440 /*! Create new tmp variable cligen object
441 * It must be filled in by later functions.
442 * The reason is, the variable must be completely parsed by successive syntax
443 * (eg <type:> stuff) and we cant insert it into the object tree until that is done.
444 * And completed by the '_post' function
445 * @param[in] cy CLIgen yacc parse struct
446 * @retval tmp variable object
447 * @see cgy_var_post
448 */
449 static cg_obj *
cgy_var_create(cligen_yacc * cy)450 cgy_var_create(cligen_yacc *cy)
451 {
452 cg_obj *co;
453
454 /* Create unassigned variable object */
455 if ((co = cov_new(CGV_ERR, NULL)) == NULL){
456 fprintf(stderr, "%s: malloc: %s\n", __FUNCTION__, strerror(errno));
457 cligen_parseerror1(cy, "Allocating cligen object");
458 return NULL;
459 }
460 if (debug)
461 fprintf(stderr, "%s: pre\n", __FUNCTION__);
462 return co;
463 }
464
465 /*! Set name and type on a (previously created) variable
466 * @param[in] cy CLIgen yacc parse struct
467 * @see cgy_var_create
468 */
469 static int
cgy_var_name_type(cligen_yacc * cy,char * name,char * type)470 cgy_var_name_type(cligen_yacc *cy,
471 char *name,
472 char *type)
473 {
474 cy->cy_var->co_command = name;
475 if ((cy->cy_var->co_vtype = cv_str2type(type)) == CGV_ERR){
476 cligen_parseerror1(cy, "Invalid type");
477 fprintf(stderr, "%s: Invalid type: %s\n", __FUNCTION__, type);
478 return -1;
479 }
480 return 0;
481 }
482
483 /*! Complete variable cligen object after parsing is complete,
484 * And insert it into object hierarchies.
485 * That is, insert a variable in each hieracrhy.
486 * @param[in] cy CLIgen yacc parse struct
487 * @retval 0 on OK
488 * @retval -1 on error
489 */
490 static int
cgy_var_post(cligen_yacc * cy)491 cgy_var_post(cligen_yacc *cy)
492 {
493 struct cgy_list *cl;
494 cg_obj *coc = NULL; /* variable copy object */
495 cg_obj *coparent; /* parent */
496 cg_obj *co; /* new obj/sister */
497 cg_obj *coy = cy->cy_var;
498
499 #if 0
500 if (coy->co_vtype == CGV_ERR) /* unassigned */
501 coy->co_vtype = cv_str2type(coy->co_command);
502 #endif
503 if (debug)
504 fprintf(stderr, "%s: cmd:%s vtype:%d\n", __FUNCTION__,
505 coy->co_command,
506 coy->co_vtype );
507 if (coy->co_vtype == CGV_ERR){
508 cligen_parseerror1(cy, "Wrong or unassigned variable type");
509 return -1;
510 }
511 #if 0 /* XXX dont really know what i am doing but variables dont behave nice in choice */
512 if (cy->cy_opt){ /* get coparent from stack */
513 if (cy->cy_stack == NULL){
514 fprintf(stderr, "Option allowed only within () or []\n");
515 return -1;
516 }
517 cl = cy->cy_stack->cs_list;
518 }
519 else
520 #endif
521 cl = cy->cy_list;
522 for (; cl; cl = cl->cl_next){
523 coparent = cl->cl_obj;
524 if (cl->cl_next){
525 if (co_copy(coy, coparent, &coc) < 0) /* duplicate coy to coc */
526 return -1;
527 }
528 else
529 coc = coy; /* Dont copy if last in list */
530 co_up_set(coc, coparent);
531 if ((co = co_insert(co_pt_get(coparent), coc)) == NULL) /* coc may be deleted */
532 return -1;
533 cl->cl_obj = co;
534 }
535 return 0;
536 }
537
538 /*! Create a new command object.
539 * Actually, create a new for every tree in the list
540 * and replace the old with the new object.
541 * @param[in] cy CLIgen yacc parse struct
542 * @param[in] cmd the command string
543 * @retval 0 OK
544 * @retval -1 Error
545 */
546 static int
cgy_cmd(cligen_yacc * cy,char * cmd)547 cgy_cmd(cligen_yacc *cy,
548 char *cmd)
549 {
550 struct cgy_list *cl;
551 cg_obj *cop; /* parent */
552 cg_obj *conew; /* new obj */
553 cg_obj *co; /* new/sister */
554
555 for (cl=cy->cy_list; cl; cl = cl->cl_next){
556 cop = cl->cl_obj;
557 if (debug)
558 fprintf(stderr, "%s: %s parent:%s\n",
559 __FUNCTION__, cmd, cop->co_command);
560 if ((conew = co_new(cmd, cop)) == NULL) {
561 cligen_parseerror1(cy, "Allocating cligen object");
562 return -1;
563 }
564 if ((co = co_insert(co_pt_get(cop), conew)) == NULL) /* co_new may be deleted */
565 return -1;
566 cl->cl_obj = co; /* Replace parent in cgy_list */
567 }
568 return 0;
569 }
570
571 /*! Create a REFERENCE node that references another tree.
572 * This is evaluated in runtime by pt_expand().
573 * @param[in] cy CLIgen yacc parse struct
574 * @see also db2tree() in clicon/apps/cli_main.c on how to create such a tree
575 * @see pt_expand_treeref()/pt_callback_reference() how it is expanded
576 */
577 static int
cgy_reference(cligen_yacc * cy,char * name)578 cgy_reference(cligen_yacc *cy,
579 char *name)
580 {
581 struct cgy_list *cl;
582 cg_obj *cop; /* parent */
583 cg_obj *cot; /* tree */
584
585 for (cl=cy->cy_list; cl; cl = cl->cl_next){
586 /* Add a treeref 'stub' which is expanded in pt_expand to a sub-tree */
587 cop = cl->cl_obj;
588 if ((cot = co_new(name, cop)) == NULL) {
589 cligen_parseerror1(cy, "Allocating cligen object");
590 return -1;
591 }
592 cot->co_type = CO_REFERENCE;
593 if ((cot = co_insert(co_pt_get(cop), cot)) == NULL) /* cot may be deleted */
594 return -1;
595 /* Replace parent in cgy_list: not allowed after ref?
596 but only way to add callbacks to it.
597 */
598 cl->cl_obj = cot;
599 }
600 return 0;
601 }
602
603 /*! Add comment
604 * Assume comment is malloced and not freed by parser
605 * @param[in] cy CLIgen yacc parse struct
606 * @param[in] comment Help string, inside ("...")
607 */
608 static int
cgy_comment(cligen_yacc * cy,char * comment)609 cgy_comment(cligen_yacc *cy,
610 char *comment)
611 {
612 struct cgy_list *cl;
613 cg_obj *co;
614
615 for (cl = cy->cy_list; cl; cl = cl->cl_next){
616 co = cl->cl_obj;
617 #ifdef CO_HELPVEC
618 if (co->co_helpvec == NULL && /* Why would it already have a comment? */
619 cligen_txt2cvv(comment, &co->co_helpvec) < 0){ /* Or just append to existing? */
620 cligen_parseerror1(cy, "Allocating comment");
621 return -1;
622 }
623 #else
624 if (co->co_help == NULL) /* Why would it already have a comment? */
625 if ((co->co_help = strdup(comment)) == NULL){
626 cligen_parseerror1(cy, "Allocating comment");
627 return -1;
628 }
629 #endif
630 }
631 return 0;
632 }
633
634 /*! Append a new choice option to a choice variable string
635 * @param[in] cy CLIgen yacc parse struct
636 * @param[in] str Accumulated choice string on the form: "a|..|z"
637 * @param[in] app Choice option to append
638 * @retval s New string created by appending: "<str>|<app>"
639 * This is just string manipulation, the complete string is linked into CLIgen structs by upper rule
640 * Note that this is variable choice. There is also command choice, eg [a|b] which is different.
641 */
642 static char *
cgy_var_choice_append(cligen_yacc * cy,char * str,char * app)643 cgy_var_choice_append(cligen_yacc *cy,
644 char *str,
645 char *app)
646 {
647 int len;
648 char *s;
649
650 len = strlen(str)+strlen(app) + 2;
651 if ((s = realloc(str, len)) == NULL) {
652 fprintf(stderr, "%s: realloc: %s\n", __FUNCTION__, strerror(errno));
653 return NULL;
654 }
655 strncat(s, "|", len-1);
656 strncat(s, app, len-1);
657 return s;
658 }
659
660 /*! Post-processing of commands, eg at ';':
661 * a cmd;<--
662 * But only if parsing succesful.
663 * 1. Add callback and args to every list
664 * 2. Add empty child unless already empty child
665 * @param[in] cy CLIgen yacc parse struct
666 */
667 int
cgy_terminal(cligen_yacc * cy)668 cgy_terminal(cligen_yacc *cy)
669 {
670 struct cgy_list *cl;
671 cg_obj *co;
672 int i;
673 struct cg_callback *cc;
674 struct cg_callback **ccp;
675 int retval = -1;
676 parse_tree *ptc;
677
678 for (cl = cy->cy_list; cl; cl = cl->cl_next){
679 co = cl->cl_obj;
680 if (cy->cy_callbacks){ /* callbacks */
681 ccp = &co->co_callbacks;
682 while (*ccp != NULL)
683 ccp = &((*ccp)->cc_next);
684 if (co_callback_copy(cy->cy_callbacks, ccp) < 0)
685 goto done;
686 }
687 /* variables: special case "hide" */
688 if (cy->cy_cvec){
689 if (cvec_find(cy->cy_cvec, "hide") != NULL)
690 co_flags_set(co, CO_FLAGS_HIDE);
691 /* generic variables */
692 if ((co->co_cvec = cvec_dup(cy->cy_cvec)) == NULL){
693 fprintf(stderr, "%s: cvec_dup: %s\n", __FUNCTION__, strerror(errno));
694 goto done;
695 }
696 }
697 /* misc */
698 if ((ptc = co_pt_get(co)) != NULL){
699 for (i=0; i<pt_len_get(ptc); i++){
700 if (pt_vec_i_get(ptc, i) == NULL)
701 break;
702 }
703 if (i == pt_len_get(ptc)) /* Insert empty child if ';' */
704 co_insert(co_pt_get(co), NULL);
705 }
706 else
707 co_insert(co_pt_get(co), NULL);
708 }
709 /* cleanup */
710 while ((cc = cy->cy_callbacks) != NULL){
711 if (cc->cc_cvec)
712 cvec_free(cc->cc_cvec);
713 if (cc->cc_fn_str)
714 free(cc->cc_fn_str);
715 cy->cy_callbacks = cc->cc_next;
716 free(cc);
717 }
718 if (cy->cy_cvec){
719 cvec_free(cy->cy_cvec);
720 cy->cy_cvec = NULL;
721 }
722 retval = 0;
723 done:
724 return retval;
725 }
726
727 /*! Take the whole cgy_list and push it to the stack
728 * @param[in] cy CLIgen yacc parse struct
729 */
730 static int
ctx_push(cligen_yacc * cy,int sets)731 ctx_push(cligen_yacc *cy,
732 int sets)
733 {
734 struct cgy_list *cl;
735 struct cgy_stack *cs;
736 cg_obj *co;
737
738 if (debug)
739 fprintf(stderr, "%s sets:%d\n", __FUNCTION__, sets);
740 /* Create new stack element */
741 if ((cs = malloc(sizeof(*cs))) == NULL) {
742 fprintf(stderr, "%s: malloc: %s\n", __FUNCTION__, strerror(errno));
743 return -1;
744 }
745 memset(cs, 0, sizeof(*cs));
746 cs->cs_next = cy->cy_stack;
747 cy->cy_stack = cs; /* Push the new stack element */
748 for (cl = cy->cy_list; cl; cl = cl->cl_next){
749 co = cl->cl_obj;
750 if (cvec_find(cy->cy_cvec, "hide") != NULL)
751 co_flags_set(co, CO_FLAGS_HIDE);
752 if (sets)
753 co_sets_set(co, 1);
754 if (cgy_list_push(co, &cs->cs_list) < 0)
755 return -1;
756 }
757 return 0;
758 }
759
760 /*! Peek context from stack and replace the object list with it
761 * Typically done in a command choice, eg "(c1|c2)" at c2.
762 * Dont pop context
763 * @param[in] cy CLIgen yacc parse struct
764 * @see ctx_peek_swap2
765 */
766 static int
ctx_peek_swap(cligen_yacc * cy)767 ctx_peek_swap(cligen_yacc *cy)
768 {
769 struct cgy_stack *cs;
770 struct cgy_list *cl;
771 cg_obj *co;
772
773 if (debug)
774 fprintf(stderr, "%s\n", __FUNCTION__);
775 if ((cs = cy->cy_stack) == NULL){
776 #if 1
777 cligen_parseerror1(cy, "No surrounding () or []");
778 return -1; /* e.g a|b instead of (a|b) */
779 #else
780 cgy_list_delete(&cy->cy_list);
781 return 0;
782 #endif
783 }
784 for (cl = cy->cy_list; cl; cl = cl->cl_next){
785 co = cl->cl_obj;
786 co_flags_set(co, CO_FLAGS_OPTION);
787 if (cgy_list_push(co, &cs->cs_saved) < 0)
788 return -1;
789 }
790 cgy_list_delete(&cy->cy_list);
791 for (cl = cs->cs_list; cl; cl = cl->cl_next){
792 co = cl->cl_obj;
793 if (cgy_list_push(co, &cy->cy_list) < 0)
794 return -1;
795 }
796 return 0;
797 }
798
799 /*! Peek context from stack and replace the object list with it
800 * Typically done in a command choice, eg "(c1|c2)" at c2.
801 * Dont pop context
802 * @param[in] cy CLIgen yacc parse struct
803 * @see ctx_peek_swap
804 */
805 static int
ctx_peek_swap2(cligen_yacc * cy)806 ctx_peek_swap2(cligen_yacc *cy)
807 {
808 struct cgy_stack *cs;
809 struct cgy_list *cl;
810 cg_obj *co;
811
812 if (debug)
813 fprintf(stderr, "%s\n", __FUNCTION__);
814 if ((cs = cy->cy_stack) == NULL){
815 #if 1
816 cligen_parseerror1(cy, "No surrounding () or []");
817 return -1; /* e.g a|b instead of (a|b) */
818 #else
819 cgy_list_delete(&cy->cy_list);
820 return 0;
821 #endif
822 }
823 cgy_list_delete(&cy->cy_list);
824 for (cl = cs->cs_list; cl; cl = cl->cl_next){
825 co = cl->cl_obj;
826 if (cgy_list_push(co, &cy->cy_list) < 0)
827 return -1;
828 }
829 return 0;
830 }
831
832 static int
delete_stack_element(struct cgy_stack * cs)833 delete_stack_element(struct cgy_stack *cs)
834 {
835 cgy_list_delete(&cs->cs_list);
836 cgy_list_delete(&cs->cs_saved);
837 free(cs);
838
839 return 0;
840 }
841
842 /*! Pop context from stack and add it to object list
843 * Done after an option, eg "cmd [opt]"
844 * "cmd <push> [opt] <pop>". At pop, all objects saved at push
845 * are added to the object list.
846 * @param[in] cy CLIgen yacc parse struct
847 */
848 static int
ctx_pop_add(cligen_yacc * cy)849 ctx_pop_add(cligen_yacc *cy)
850 {
851 struct cgy_stack *cs;
852 struct cgy_list *cl;
853 cg_obj *co;
854
855 if (debug)
856 fprintf(stderr, "%s\n", __FUNCTION__);
857 for (cl = cy->cy_list; cl; cl = cl->cl_next){
858 co = cl->cl_obj;
859 co_flags_set(co, CO_FLAGS_OPTION);
860 }
861 if ((cs = cy->cy_stack) == NULL){
862 fprintf(stderr, "%s: cgy_stack empty\n", __FUNCTION__);
863 return -1; /* shouldnt happen */
864 }
865 for (cl = cs->cs_list; cl; cl = cl->cl_next){
866 co = cl->cl_obj;
867 }
868 cy->cy_stack = cs->cs_next;
869 /* We could have saved some heap work by moving the cs_list,... */
870 for (cl = cs->cs_list; cl; cl = cl->cl_next){
871 co = cl->cl_obj;
872 if (cgy_list_push(co, &cy->cy_list) < 0)
873 return -1;
874 }
875 for (cl = cs->cs_saved; cl; cl = cl->cl_next){
876 co = cl->cl_obj;
877 if (cgy_list_push(co, &cy->cy_list) < 0)
878 return -1;
879 }
880 delete_stack_element(cs);
881 return 0;
882 }
883
884 /*! Pop context from stack and discard it.
885 * Typically done after a grouping, eg "cmd (opt1|opt2)"
886 * @param[in] cy CLIgen yacc parse struct
887 */
888 static int
ctx_pop(cligen_yacc * cy)889 ctx_pop(cligen_yacc *cy)
890 {
891 struct cgy_stack *cs;
892 struct cgy_list *cl;
893 cg_obj *co;
894
895 if (debug)
896 fprintf(stderr, "%s\n", __FUNCTION__);
897 if ((cs = cy->cy_stack) == NULL){
898 fprintf(stderr, "%s: cgy_stack empty\n", __FUNCTION__);
899 return -1; /* shouldnt happen */
900 }
901 for (cl = cs->cs_list; cl; cl = cl->cl_next)
902 co = cl->cl_obj;
903 cy->cy_stack = cs->cs_next;
904 for (cl = cs->cs_saved; cl; cl = cl->cl_next){
905 co = cl->cl_obj;
906 if (cgy_list_push(co, &cy->cy_list) < 0)
907 return -1;
908 }
909 delete_stack_element(cs);
910 return 0;
911 }
912
913 /*!
914 * @param[in] cy CLIgen yacc parse struct
915 */
916 static int
cg_regexp(cligen_yacc * cy,char * rx,int invert)917 cg_regexp(cligen_yacc *cy,
918 char *rx,
919 int invert)
920 {
921 int retval = -1;
922 cg_var *cv;
923
924 if (cy->cy_var->co_regex == NULL &&
925 (cy->cy_var->co_regex = cvec_new(0)) == NULL)
926 goto done;
927 if ((cv = cvec_add(cy->cy_var->co_regex, CGV_STRING)) == NULL)
928 goto done;
929 if (invert)
930 cv_flag_set(cv, V_INVERT);
931 cv_string_set(cv, rx);
932 if (cy->cy_var->co_vtype != CGV_STRING && cy->cy_var->co_vtype != CGV_REST)
933 cy->cy_var->co_vtype = CGV_STRING;
934 retval = 0;
935 done:
936 return retval;
937 }
938
939 /*! Given an optional min and a max, create low and upper bounds on cv values
940 *
941 * @param[in] cy CLIgen yacc parse struct
942 * @param[in] lowstr low bound of interval (can be NULL)
943 * @param[in] uppstr upper bound of interval
944 * @param[in] yv The CLIgen syntax object
945 * @param[in] cvtype Type of variable
946 * Supported for ints, decimal64 and strings.
947 * <x:int length[min:max]> or <x:int length[max]>
948 * @note: decimal64 fraction-digits must be given before range:
949 * <x:decimal64 fraction-digits:4 range[20.0]>
950 * if you want any other fraction-digit than 2
951 */
952 static int
cg_range_create(cligen_yacc * cy,char * lowstr,char * uppstr,cg_obj * yv,enum cv_type cvtype)953 cg_range_create(cligen_yacc *cy,
954 char *lowstr,
955 char *uppstr,
956 cg_obj *yv,
957 enum cv_type cvtype)
958 {
959 int retval = -1;
960 char *reason = NULL;
961 cg_var *cv1 = NULL;
962 cg_var *cv2 = NULL;
963 int cvret;
964
965 /* First create low bound cv */
966 if ((cv1 = cv_new(cvtype)) == NULL){
967 fprintf(stderr, "cv_new %s\n", strerror(errno));
968 goto done;
969 }
970 if (cv_name_set(cv1, "range_low") == NULL){
971 fprintf(stderr, "cv_name_set %s\n", strerror(errno));
972 goto done;
973 }
974 if (lowstr == NULL){
975 cv_type_set(cv1, CGV_EMPTY);
976 }
977 else{
978 if (yv->co_vtype == CGV_DEC64) /* XXX: Seems misplaced? / too specific */
979 cv_dec64_n_set(cv1, yv->co_dec64_n);
980 if ((cvret = cv_parse1(lowstr, cv1, &reason)) < 0){
981 fprintf(stderr, "cv_parse1 %s\n", strerror(errno));
982 goto done;
983 }
984 if (cvret == 0){ /* parsing failed */
985 cligen_parseerror1(cy, reason);
986 free(reason);
987 goto done;
988 }
989 }
990 /* Then append it to the lowbound cvec, create if NULL */
991 if (yv->co_rangecvv_low == NULL){
992 if ((yv->co_rangecvv_low = cvec_from_var(cv1)) == NULL)
993 goto done;
994 }
995 else if (cvec_append_var(yv->co_rangecvv_low, cv1) < 0)
996 goto done;
997
998 /* Then create upper bound cv */
999 if ((cv2 = cv_new(cvtype)) == NULL){
1000 fprintf(stderr, "cv_new %s\n", strerror(errno));
1001 goto done;
1002 }
1003 if (cv_name_set(cv2, "range_high") == NULL){
1004 fprintf(stderr, "cv_name_set %s\n", strerror(errno));
1005 goto done;
1006 }
1007 if (yv->co_vtype == CGV_DEC64) /* XXX: Seems misplaced? / too specific */
1008 cv_dec64_n_set(cv2, yv->co_dec64_n);
1009 if ((cvret = cv_parse1(uppstr, cv2, &reason)) < 0){
1010 fprintf(stderr, "cv_parse1 %s\n", strerror(errno));
1011 goto done;
1012 }
1013 if (cvret == 0){ /* parsing failed */
1014 cligen_parseerror1(cy, reason);
1015 free(reason);
1016 goto done;
1017 }
1018
1019 /* Append it to the upper bound cvec, create if NULL */
1020 if (yv->co_rangecvv_upp == NULL){
1021 if ((yv->co_rangecvv_upp = cvec_from_var(cv2)) == NULL)
1022 goto done;
1023 }
1024 else if (cvec_append_var(yv->co_rangecvv_upp, cv2) < 0)
1025 goto done;
1026
1027 /* Then increment range vector length */
1028 yv->co_rangelen++;
1029 retval = 0;
1030 done:
1031 if (cv1)
1032 cv_free(cv1);
1033 if (cv2)
1034 cv_free(cv2);
1035 return retval;
1036 }
1037
1038 /*! A length statement has been parsed. Create length/range cv
1039 *
1040 * @param[in] cy CLIgen yacc parse struct
1041 * @param[in] lowstr low bound of interval (can be NULL)
1042 * @param[in] uppstr upper bound of interval
1043 * Examples:
1044 * <x:string length[min:max]> <x:string length[max]>
1045 * @note that the co_range structure fields are re-used for string length restrictions.
1046 * but the range type is uint64, not depending on cv type as int:s
1047 * @see cg_range
1048 */
1049 static int
cg_length(cligen_yacc * cy,char * lowstr,char * uppstr)1050 cg_length(cligen_yacc *cy,
1051 char *lowstr,
1052 char *uppstr)
1053 {
1054 cg_obj *yv;
1055
1056 if ((yv = cy->cy_var) == NULL){
1057 fprintf(stderr, "No var obj");
1058 return -1;
1059 }
1060 return cg_range_create(cy, lowstr, uppstr, yv, CGV_UINT64);
1061 }
1062
1063 /*! A range statement has been parsed. Create range cv
1064 * @param[in] cy CLIgen yacc parse struct
1065 * @param[in] lowstr low bound of interval (can be NULL)
1066 * @param[in] uppstr upper bound of interval
1067 * Examples:
1068 * <x:int32 range[min:max]> <x:int32 range[max]>
1069 * @see cg_length
1070 */
1071 static int
cg_range(cligen_yacc * cy,char * lowstr,char * uppstr)1072 cg_range(cligen_yacc *cy,
1073 char *lowstr,
1074 char *uppstr)
1075 {
1076 cg_obj *yv;
1077
1078 if ((yv = cy->cy_var) == NULL){
1079 fprintf(stderr, "No var obj");
1080 return -1;
1081 }
1082 return cg_range_create(cy, lowstr, uppstr, yv, yv->co_vtype);
1083 }
1084
1085 /*!
1086 * @param[in] cy CLIgen yacc parse struct
1087 */
1088 static int
cg_dec64_n(cligen_yacc * cy,char * fraction_digits)1089 cg_dec64_n(cligen_yacc *cy,
1090 char *fraction_digits)
1091 {
1092 cg_obj *yv;
1093 char *reason = NULL;
1094
1095 if ((yv = cy->cy_var) == NULL){
1096 fprintf(stderr, "No var obj");
1097 return -1;
1098 }
1099 if (parse_uint8(fraction_digits, &yv->co_dec64_n, NULL) != 1){
1100 cligen_parseerror1(cy, reason);
1101 return -1;
1102 }
1103 return 0;
1104 }
1105
1106 /*!
1107 * @param[in] cy CLIgen yacc parse struct
1108 */
1109 int
cgy_init(cligen_yacc * cy,cg_obj * co_top)1110 cgy_init(cligen_yacc *cy,
1111 cg_obj *co_top)
1112 {
1113 if (debug)
1114 fprintf(stderr, "%s\n", __FUNCTION__);
1115 /* Add top-level object */
1116 if (cgy_list_push(co_top, &cy->cy_list) < 0)
1117 return -1;
1118 if (ctx_push(cy, 0) < 0)
1119 return -1;
1120 return 0;
1121 }
1122
1123 /*!
1124 * @param[in] cy CLIgen yacc parse struct
1125 */
1126 int
cgy_exit(cligen_yacc * cy)1127 cgy_exit(cligen_yacc *cy)
1128 {
1129 struct cgy_stack *cs;
1130
1131 if (debug)
1132 fprintf(stderr, "%s\n", __FUNCTION__);
1133
1134 cy->cy_var = NULL;
1135 cgy_list_delete(&cy->cy_list);
1136 if((cs = cy->cy_stack) != NULL){
1137 delete_stack_element(cs);
1138 #if 0
1139 fprintf(stderr, "%s:%d: error: lacking () or [] at or before: '%s'\n",
1140 cy->cy_name,
1141 cy->cy_linenum,
1142 cy->cy_parse_string
1143 );
1144 return -1;
1145 #endif
1146 }
1147 return 0;
1148 }
1149
1150 %}
1151
1152 %%
1153
1154 file : lines MY_EOF{if(debug)printf("file->lines\n"); YYACCEPT;}
1155 ;
1156
1157 lines : lines line {
1158 if(debug)printf("lines->lines line\n");
1159 }
1160 | { if(debug)printf("lines->\n"); }
1161 ;
1162
1163 line : decltop line1 { if (debug) printf("line->decltop line1\n"); }
1164 | assignment ';' { if (debug) fprintf(stderr, "line->assignment ;\n"); }
1165 ;
1166
1167 line1 : line2 { if (debug) printf("line1->line2\n"); }
1168 | ',' options line2 { if (debug) printf("line1->',' options line2\n"); }
1169 ;
1170
1171 preline : '{' { $$ = 0; }
1172 | SETS { $$ = 1; } /* SETS */
1173 ;
1174
1175 line2 : ';' {
1176 if (debug) printf("line2->';'\n");
1177 if (cgy_terminal(_cy) < 0) YYERROR;
1178 if (ctx_peek_swap2(_cy) < 0) YYERROR;
1179 }
1180 | preline {
1181 if (ctx_push(_cy, $1) < 0) YYERROR;
1182 }
1183 lines
1184 '}' {
1185 if (debug) printf("line2->'{' lines '}'\n");
1186 if (ctx_pop(_cy) < 0) YYERROR;
1187 if (ctx_peek_swap2(_cy) < 0) YYERROR;
1188 }
1189 | ';'
1190 preline {
1191 if (cgy_terminal(_cy) < 0) YYERROR;
1192 if (ctx_push(_cy, $2) < 0) YYERROR;
1193 }
1194 lines
1195 '}' { if (debug) printf("line2->';' '{' lines '}'\n");if (ctx_pop(_cy) < 0) YYERROR;if (ctx_peek_swap2(_cy) < 0) YYERROR; }
1196 ;
1197
1198 options : options ',' option {if (debug)printf("options->options , option\n");}
1199 | option {if (debug)printf("options->option\n");}
1200 ;
1201 option : callback {if (debug)printf("option->callback\n");}
1202 | flag {if (debug)printf("option->flag\n");}
1203 | assignment {if (debug)printf("option->assignment\n");}
1204 ;
1205
1206 assignment : NAME '=' DQ charseq DQ {cgy_assignment(_cy, $1,$4);free($1); free($4);}
1207 ;
1208
1209 flag : NAME {cgy_flag(_cy, $1);free($1);}
1210 ;
1211
1212 callback : NAME {if (cgy_callback(_cy, $1) < 0) YYERROR;} '(' arglist ')'
1213 ;
1214
1215 arglist : arglist1
1216 |
1217 ;
1218
1219 arglist1 : arglist1 ',' arg
1220 | arg
1221 ;
1222
1223 arg : typecast arg1 {
1224 if ($2 && cgy_callback_arg(_cy, $1, $2) < 0) YYERROR;
1225 if ($1 != NULL) free($1);
1226 if ($2 != NULL) free($2);
1227 }
1228 ;
1229
1230 arg1 : DQ DQ { $$=NULL; }
1231 | DQ charseq DQ { $$=$2; }
1232 | NAME { $$=$1; }
1233 ;
1234
1235 typecast : '(' NAME ')' { $$ = $2; }
1236 | { $$ = NULL; }
1237 ;
1238
1239 decltop : decllist {if (debug)fprintf(stderr, "decltop->decllist\n");}
1240 | declcomp {if (debug)fprintf(stderr, "decltop->declcomp\n");}
1241 ;
1242
1243 decllist : decltop
1244 declcomp {if (debug)fprintf(stderr, "decllist->decltop declcomp\n");}
1245 | decltop '|' { if (ctx_peek_swap(_cy) < 0) YYERROR;}
1246 declcomp {if (debug)fprintf(stderr, "decllist->decltop | declcomp\n");}
1247 ;
1248
1249 declcomp : '(' { if (ctx_push(_cy, 0) < 0) YYERROR; } decltop ')' { if (ctx_pop(_cy) < 0) YYERROR; if (debug)fprintf(stderr, "declcomp->(decltop)\n");}
1250 | '[' { if (ctx_push(_cy, 0) < 0) YYERROR; } decltop ']' { if (ctx_pop_add(_cy) < 0) YYERROR; } {if (debug)fprintf(stderr, "declcomp->[decltop]\n");}
1251 | decl {if (debug)fprintf(stderr, "declcomp->decl\n");}
1252 ;
1253
1254 decl : cmd {if (debug)fprintf(stderr, "decl->cmd\n");}
1255 | cmd PDQ charseq DQP { if (debug)fprintf(stderr, "decl->cmd (\" comment \")\n");if (cgy_comment(_cy, $3) < 0) YYERROR; free($3);}
1256 | cmd PDQ DQP { if (debug)fprintf(stderr, "decl->cmd (\"\")\n");}
1257 ;
1258
1259 cmd : NAME { if (debug)fprintf(stderr, "cmd->NAME(%s)\n", $1);if (cgy_cmd(_cy, $1) < 0) YYERROR; free($1); }
1260 | '@' NAME { if (debug)fprintf(stderr, "cmd->@NAME\n");if (cgy_reference(_cy, $2) < 0) YYERROR; free($2); }
1261 | '<' { if ((_CY->cy_var = cgy_var_create(_CY)) == NULL) YYERROR; }
1262 variable '>' { if (cgy_var_post(_cy) < 0) YYERROR; }
1263 ;
1264
1265 variable : NAME { if (cgy_var_name_type(_cy, $1, $1)<0) YYERROR; }
1266 | NAME ':' NAME { if (cgy_var_name_type(_cy, $1, $3)<0) YYERROR; free($3); }
1267 | NAME ' ' { if (cgy_var_name_type(_cy, $1, $1) < 0) YYERROR; }
1268 keypairs
1269 | NAME ':' NAME ' ' { if (cgy_var_name_type(_cy, $1, $3) < 0) YYERROR; free($3); }
1270 keypairs
1271 ;
1272
1273 keypairs : keypair
1274 | keypairs ' ' keypair
1275 ;
1276
1277 numdec : NUMBER { $$ = $1; }
1278 | DECIMAL
1279 ;
1280
1281 keypair : NAME '(' ')' { expand_fn(_cy, $1); }
1282 | NAME '(' exparglist ')' { expand_fn(_cy, $1); }
1283 | V_SHOW ':' NAME {
1284 _CY->cy_var->co_show = $3;
1285 }
1286 | V_SHOW ':' DQ charseq DQ {
1287 _CY->cy_var->co_show = $4;
1288 }
1289 | V_RANGE '[' numdec ':' numdec ']' {
1290 if (cg_range(_cy, $3, $5) < 0) YYERROR; free($3); free($5);
1291 }
1292 | V_RANGE '[' numdec ']' {
1293 if (cg_range(_cy, NULL, $3) < 0) YYERROR; free($3);
1294 }
1295 | V_LENGTH '[' NUMBER ':' NUMBER ']' {
1296 if (cg_length(_cy, $3, $5) < 0) YYERROR; free($3); free($5);
1297 }
1298 | V_LENGTH '[' NUMBER ']' {
1299 if (cg_length(_cy, NULL, $3) < 0) YYERROR; free($3);
1300 }
1301 | V_FRACTION_DIGITS ':' NUMBER {
1302 if (cg_dec64_n(_cy, $3) < 0) YYERROR; free($3);
1303 }
1304 | V_CHOICE choices { _CY->cy_var->co_choice = $2; }
1305 | V_KEYWORD ':' NAME {
1306 _CY->cy_var->co_keyword = $3;
1307 _CY->cy_var->co_vtype=CGV_STRING;
1308 }
1309 | V_REGEXP ':' DQ charseq DQ { if (cg_regexp(_cy, $4, 0) < 0) YYERROR; free($4); }
1310 | V_REGEXP ':' '!' DQ charseq DQ { if (cg_regexp(_cy, $5, 1) < 0) YYERROR; free($5);}
1311 | V_TRANSLATE ':' NAME '(' ')' { cg_translate(_cy, $3); }
1312 ;
1313
1314 exparglist : exparglist ',' exparg
1315 | exparg
1316 ;
1317
1318 exparg : DQ DQ
1319 | DQ charseq DQ { expand_arg(_cy, "string", $2); free($2); }
1320 ;
1321
1322 exparg : typecast arg1 {
1323 if ($2 && cgy_callback_arg(_cy, $1, $2) < 0) YYERROR;
1324 if ($1) free($1);
1325 if ($2) free($2);
1326 }
1327 ;
1328
1329 choices : { $$ = NULL;}
1330 | NUMBER { $$ = $1;}
1331 | NAME { $$ = $1;}
1332 | DECIMAL { $$ = $1;}
1333 | choices '|' NUMBER { $$ = cgy_var_choice_append(_cy, $1, $3); free($3);}
1334 | choices '|' NAME { $$ = cgy_var_choice_append(_cy, $1, $3); free($3);}
1335 | choices '|' DECIMAL { $$ = cgy_var_choice_append(_cy, $1, $3); free($3);}
1336 ;
1337
1338 charseq : charseq CHARS
1339 {
1340 int len = strlen($1);
1341 $$ = realloc($1, len+strlen($2) +1);
1342 sprintf($$+len, "%s", $2);
1343 free($2);
1344 }
1345
1346 | CHARS {$$=$1;}
1347 ;
1348
1349
1350 %%
1351
1352