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