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