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 #include "cligen_config.h"
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdint.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <inttypes.h>
41 #include <unistd.h>
42 #include <sys/types.h>
43 #include <sys/socket.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
46 
47 #include "cligen_buf.h"
48 #include "cligen_cv.h"
49 #include "cligen_cvec.h"
50 #include "cligen_parsetree.h"
51 #include "cligen_pt_head.h"
52 #include "cligen_object.h"
53 #include "cligen_handle.h"
54 #include "cligen_print.h"
55 
56 #define VARIABLE_PRE  '<'
57 #define VARIABLE_POST '>'
58 
59 
60 /* Static prototypes */
61 static int pt2cbuf(cbuf *cb, parse_tree *pt, int level, int brief);
62 
63 /*! Print the syntax specification of a variable syntax spec to a cligen buf
64  *
65  * @param[out] cb    CLIgen buffer string to print to
66  * @param[in]  co    Cligen object from where to print
67  * @param[in]  brief If set, just <varname>, otherwise clispec parsable format
68  * @retval     0     OK
69  * @retval    -1     Error
70  * @code
71  *    cbuf *cb = cbuf_new();
72  *    cov2cbuf(cb, co, brief);
73  *    cbuf_free(cb);
74  * @endcode
75  *
76  * Brief output omits help-strings and variable options except names. Eg:
77  * brief=0:  a("help string") <x:int32>("variable"), cb();
78  * brief=1:  a <x>;
79  */
80 int
cov2cbuf(cbuf * cb,cg_obj * co,int brief)81 cov2cbuf(cbuf   *cb,
82 	 cg_obj *co,
83 	 int     brief)
84 {
85     int     retval = -1;
86     int     i;
87     cg_var *cv1;
88     cg_var *cv2;
89 
90     if (co->co_choice){
91 	if (strchr(co->co_choice, '|'))
92 	    cprintf(cb, "(%s)", co->co_choice);
93 	else
94 	    cprintf(cb, "%s", co->co_choice);
95     }
96     else{
97 	if (brief){
98 	    cprintf(cb, "%c%s%c", VARIABLE_PRE,
99 		    co->co_show!=NULL ? co->co_show : co->co_command,
100 		    VARIABLE_POST);
101 	}
102 	else{
103 	    cprintf(cb, "%c%s:%s", VARIABLE_PRE, co->co_command, cv_type2str(co->co_vtype));
104 
105 	    for (i=0; i<co->co_rangelen; i++){
106 		if (cv_isint(co->co_vtype))
107 		    cprintf(cb, " range[");
108 		else
109 		    cprintf(cb, " length[");
110 		cv1 = cvec_i(co->co_rangecvv_low, i);
111 		cv2 = cvec_i(co->co_rangecvv_upp, i);
112 		if (cv_type_get(cv1) != CGV_EMPTY){
113 		    cv2cbuf(cv1, cb);
114 		    cprintf(cb, ":");
115 		}
116 		cv2cbuf(cv2, cb);
117 		cprintf(cb, "]");
118 	    }
119 
120 	    if (co->co_show)
121 		cprintf(cb, " show:\"%s\"", co->co_show);
122 	    if (co->co_expand_fn_str){
123 		cprintf(cb, " %s(\"", co->co_expand_fn_str);
124 		if (co->co_expand_fn_vec)
125 		    cvec2cbuf(cb, co->co_expand_fn_vec);
126 		cprintf(cb, "\")");
127 	    }
128 	    cv1 = NULL;
129 	    while ((cv1 = cvec_each(co->co_regex, cv1)) != NULL)
130 		cprintf(cb, " regexp:\"%s\"", cv_string_get(cv1));
131 	    if (co->co_translate_fn_str)
132 		cprintf(cb, " translate:%s()", co->co_translate_fn_str);
133 	    cprintf(cb, "%c", VARIABLE_POST);
134 	}
135     }
136     retval = 0;
137 //  done:
138 
139     return retval;
140 }
141 
142 /*! co is a terminal command, and therefore should be printed with a ';' */
143 static int
terminal(cg_obj * co)144 terminal(cg_obj *co)
145 {
146     parse_tree *pt;
147 
148     pt = co_pt_get(co);
149     return ((pt_len_get(pt)>0 && pt_vec_i_get(pt, 0) == NULL) ||
150 	    pt_len_get(pt) == 0);
151 }
152 
153 /*! Print a CLIgen object (cg object / co) to a CLIgen buffer
154  */
155 static int
co2cbuf(cbuf * cb,cg_obj * co,int marginal,int brief)156 co2cbuf(cbuf   *cb,
157 	cg_obj *co,
158 	int     marginal,
159 	int     brief)
160 {
161     int                 retval = -1;
162     struct cg_callback *cc;
163     parse_tree         *pt;
164     cg_var             *cv;
165     int                 i;
166 
167     switch (co->co_type){
168     case CO_COMMAND:
169 	if (co->co_command)
170 	    cprintf(cb, "%s", co->co_command);
171 	break;
172     case CO_REFERENCE:
173 	if (co->co_command)
174 	    cprintf(cb, "@%s", co->co_command);
175 	break;
176     case CO_VARIABLE:
177 	cov2cbuf(cb, co, brief);
178 	break;
179     }
180     if (brief == 0){
181 #ifdef CO_HELPVEC
182 	if (co->co_helpvec){
183 	    cprintf(cb, "(\"");
184 	    cv = NULL;
185 	    i = 0;
186 	    while ((cv = cvec_each(co->co_helpvec, cv)) != NULL) {
187 		if (i++)
188 		    cprintf(cb, "\n");
189 		cv2cbuf(cv, cb);
190 	    }
191 	    cprintf(cb, "\")");
192 	}
193 #else
194 	if (co->co_help)
195 	    cprintf(cb, "(\"%s\")", co->co_help);
196 #endif
197 	if (co_flags_get(co, CO_FLAGS_HIDE))
198 	    cprintf(cb, ", hide");
199 	for (cc = co->co_callbacks; cc; cc=cc->cc_next){
200 	    if (cc->cc_fn_str){
201 		cprintf(cb, ", %s(", cc->cc_fn_str);
202 		if (cc->cc_cvec){
203 		    cv = NULL;
204 		    i = 0;
205 		    while ((cv = cvec_each(cc->cc_cvec, cv)) != NULL) {
206 			if (i++)
207 			    cprintf(cb, ",");
208 			cprintf(cb, "\"%s\"", cv_string_get(cv));
209 		    }
210 		}
211 		cprintf(cb, ")");
212 	    }
213 	}
214     }
215     if (terminal(co))
216 	cprintf(cb, ";");
217     pt = co_pt_get(co);
218     if (pt_len_get(pt)>1){
219 	if (co_sets_get(co))
220 	    cprintf(cb, "@");
221 	cprintf(cb, "{\n");
222     }
223     else
224 	if (pt_len_get(pt)==1 && pt_vec_i_get(pt, 0) != NULL)
225 	    cprintf(cb, " ");
226 	else
227 	    cprintf(cb, "\n");
228     if (pt2cbuf(cb, pt, marginal+3, brief) < 0)
229 	goto done;
230     if (pt_len_get(pt)>1){
231 	cprintf(cb, "%*s", marginal, "");
232 	cprintf(cb, "}\n");
233     }
234     retval = 0;
235   done:
236     return retval;
237 }
238 
239 /*! Print a CLIgen parse-tree to a cbuf
240  * @param[in,out] cb     CLIgen buffer
241  * @param[in]     pt     Cligen parse-tree consisting of cg objects and variables
242  * @param[in]     marginal How many columns to print
243  * @param[in]     brief  Print brief output, otherwise clispec parsable format
244  */
245 static int
pt2cbuf(cbuf * cb,parse_tree * pt,int marginal,int brief)246 pt2cbuf(cbuf       *cb,
247 	parse_tree *pt,
248 	int         marginal,
249 	int         brief)
250 {
251     int retval = -1;
252     int i;
253 
254     for (i=0; i<pt_len_get(pt); i++){
255 	if (pt_vec_i_get(pt, i) == NULL){
256 	    continue;
257 	}
258 	if (pt_len_get(pt) > 1)
259 	    cprintf(cb, "%*s", marginal, "");
260 	if (co2cbuf(cb, pt_vec_i_get(pt, i), marginal, brief) < 0)
261 	    goto done;
262     }
263     retval = 0;
264   done:
265     return retval;
266 }
267 
268 /*! Print CLIgen parse-tree to file, brief or detailed.
269  *
270  * @param[in] f      Output file
271  * @param[in] pt     Cligen parse-tree consisting of cg objects and variables
272  * @param[in] brief  Print brief output, otherwise clispec parsable format
273  *
274  * The output may not be identical to the input syntax.
275  * For example [dd|ee] is printed as:
276  *   dd;
277  *   ee:
278  * Brief output omits help-strings and variable options except names. Eg:
279  * brief=0:  a("help string") <x:int32>("variable"), cb();
280  * brief=1:  a <x>;
281  * @see co_print which prints an individual CLIgen syntax object, not vector
282  */
283 int
pt_print(FILE * f,parse_tree * pt,int brief)284 pt_print(FILE       *f,
285 	 parse_tree *pt,
286 	 int         brief)
287 {
288     int   retval = -1;
289     cbuf *cb = NULL;
290 
291     if ((cb = cbuf_new()) == NULL){
292 	fprintf(stderr, "cbuf_new: %s\n", strerror(errno));
293 	goto done;
294     }
295     if (pt2cbuf(cb, pt, 0, brief) < 0)
296 	goto done;
297     fprintf(f, "%s", cbuf_get(cb));
298     retval = 0;
299   done:
300     if (cb)
301 	cbuf_free(cb);
302     return retval;
303 }
304 
305 /*! Print CLIgen parse-tree object to file, brief or detailed.
306  *
307  * @param[in] f      Output file
308  * @param[in] co     Cligen object
309  * @param[in] brief  Print brief output, otherwise clispec parsable format
310  *
311  * @see pt_print  which prints a vector of cligen objects "parse-tree"
312  */
313 int
co_print(FILE * f,cg_obj * co,int brief)314 co_print(FILE    *f,
315 	 cg_obj  *co,
316 	 int      brief)
317 {
318     int   retval = -1;
319     cbuf *cb = NULL;
320 
321     if ((cb = cbuf_new()) == NULL){
322 	fprintf(stderr, "cbuf_new: %s\n", strerror(errno));
323 	goto done;
324     }
325     if (co2cbuf(cb, co, 0, brief) < 0)
326 	goto done;
327     fprintf(f, "%s", cbuf_get(cb));
328     retval = 0;
329   done:
330     if (cb)
331 	cbuf_free(cb);
332     return retval;
333 }
334 
335 static int co_dump1(FILE *f, cg_obj *co, int indent);
336 
337 static int
pt_dump1(FILE * f,parse_tree * pt,int indent)338 pt_dump1(FILE       *f,
339 	 parse_tree *pt,
340 	 int         indent)
341 {
342     int     i;
343     cg_obj *co;
344     char   *name;
345 
346     name = pt_name_get(pt);
347     fprintf(stderr, "%*s %p pt %s [%d]\n",
348 	    indent*3, "", pt,
349 	    name?name:"",
350 	    pt_len_get(pt));
351     for (i=0; i<pt_len_get(pt); i++){
352 	if ((co = pt_vec_i_get(pt, i)) == NULL)
353 	    fprintf(stderr, "%*s NULL\n", (indent+1)*3, "");
354 	else
355 	    co_dump1(f, co, indent+1);
356     }
357     return 0;
358 }
359 
360 static int
co_dump1(FILE * f,cg_obj * co,int indent)361 co_dump1(FILE    *f,
362 	 cg_obj  *co,
363 	 int      indent)
364 {
365     parse_tree *pt;
366 
367     switch (co->co_type){
368     case CO_COMMAND:
369 	fprintf(stderr, "%*s %p co %s\n", indent*3, "", co, co->co_command);
370 	break;
371     case CO_REFERENCE:
372 	fprintf(stderr, "%*s %p co @%s\n", indent*3, "", co, co->co_command);
373 	break;
374     case CO_VARIABLE:
375 	fprintf(stderr, "%*s %p co <%s>\n", indent*3, "", co, co->co_command);
376 	break;
377     }
378     if ((pt = co_pt_get(co)) != NULL)
379 	pt_dump1(stderr, pt, indent);
380     return 0;
381 }
382 
383 /*! Debugging function for dumping a tree:s pointers
384  */
385 int
co_dump(FILE * f,cg_obj * co)386 co_dump(FILE    *f,
387 	cg_obj  *co)
388 {
389     return co_dump1(f, co, 0);
390 }
391 
392 /*! Debugging function for dumping a tree:s pointers
393  */
394 int
pt_dump(FILE * f,parse_tree * pt)395 pt_dump(FILE       *f,
396 	parse_tree *pt)
397 {
398     return pt_dump1(f, pt, 0);
399 }
400 
401 /*! Print list of CLIgen parse-trees
402  *
403  * @param[in] f      File to print to
404  * @param[in] co     Cligen object
405  * @param[in] brief  Print brief output, otherwise clispec parsable format
406  *
407  * @see pt_print  which prints a vector of cligen objects "parse-tree"
408  */
409 int
cligen_print_trees(FILE * f,cligen_handle h,int brief)410 cligen_print_trees(FILE         *f,
411 		   cligen_handle h,
412 		   int           brief)
413 {
414     int          retval = -1;
415     pt_head     *ph;
416     parse_tree  *pt;
417 
418     ph = NULL;
419     while ((ph = cligen_ph_each(h, ph)) != NULL) {
420 	fprintf(stderr, "%s\n", cligen_ph_name_get(ph));
421 	pt = cligen_ph_parsetree_get(ph);
422 	if (!brief && pt_print(f, pt, brief) < 0)
423 	    goto done;
424     }
425     retval = 0;
426   done:
427     return retval;
428 }
429