1 /*
2 CLI generator readline. Do input processing and matching.
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 #include "cligen_config.h"
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <stdint.h>
41 #include <string.h>
42 #include <errno.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #ifdef WIN32
46 #include <winsock2.h>
47 #else
48 #include <sys/ioctl.h>
49 #include <sys/socket.h>
50 #include <netinet/in.h>
51 #include <arpa/inet.h>
52 #include <unistd.h>
53 #include <termios.h>
54 #include <fcntl.h>
55 #endif /* WIN32 */
56 #define __USE_GNU /* isblank() */
57 #include <ctype.h>
58 #include <assert.h>
59
60 #ifndef isblank
61 #define isblank(c) (c==' ')
62 #endif /* isblank */
63
64 #include "cligen_buf.h"
65 #include "cligen_cv.h"
66 #include "cligen_cvec.h"
67 #include "cligen_parsetree.h"
68 #include "cligen_pt_head.h"
69 #include "cligen_object.h"
70 #include "cligen_handle.h"
71 #include "cligen_print.h"
72 #include "cligen_io.h"
73 #include "cligen_read.h"
74 #include "cligen_match.h"
75 #include "cligen_expand.h"
76 #include "cligen_history_internal.h"
77 #include "cligen_getline.h"
78
79 /*
80 * Local prototypes
81 */
82 static int show_help_columns(cligen_handle h, FILE *fout, char *s, parse_tree *pt, cvec *cvv);
83 static int show_help_line(cligen_handle h, FILE *fout, char *s, parse_tree *pt, cvec *);
84 static int cli_complete(cligen_handle h, int *lenp, parse_tree *pt, cvec *cvv);
85
86 /*! Callback from getline: '?' has been typed on command line
87 * Just show help by calling long help show function.
88 * @param[in] string Input string to match
89 * @param[in] cursor_loc - Location of cursor
90 * @retval 0 OK: required by getline
91 * @retval -1 Error
92 * @note Flaw related to getline: Errors from sub-functions are ignored
93 * @see cli_tab_hook
94 */
95 static int
cli_qmark_hook(cligen_handle h,char * string)96 cli_qmark_hook(cligen_handle h,
97 char *string)
98 {
99 int retval = -1;
100 parse_tree *pt=NULL; /* Orig parse-tree */
101 parse_tree *ptn = NULL; /* Expanded */
102 cvec *cvv = NULL;
103
104 if ((ptn = pt_new()) == NULL)
105 goto done;
106 fputs("\n", stdout);
107 if ((pt = cligen_ph_active_get(h)) == NULL)
108 goto ok;
109 if (pt_expand_treeref(h, NULL, pt) < 0) /* sub-tree expansion */
110 goto done;
111 if ((cvv = cvec_start(string)) == NULL)
112 goto done;
113 if (pt_expand(h, pt, cvv, 1, 0, ptn) < 0) /* expansion */
114 return -1;
115 if (show_help_line(h, stdout, string, ptn, cvv) <0)
116 goto done;
117 ok:
118 retval = 0;
119 done:
120 if (cvv)
121 cvec_free(cvv);
122 if (ptn && pt_free(ptn, 0) < 0)
123 return -1;
124 if (pt && pt_expand_cleanup(pt) < 0)
125 return -1;
126 if (pt && pt_expand_treeref_cleanup(pt) < 0)
127 return -1;
128 return retval;
129 }
130
131 /*! Callback from getline: TAB has been typed on keyboard
132 * First try to complete the string if the possibilities
133 * allow that (at least one unique common character).
134 * If no completion was made, then show the command alternatives.
135 * @param[in] h CLIgen handle
136 * @param[in,out] cursorp Pointer to location of cursor on entry and exit
137 * @retval -1 Error
138 * @retval -2 (value != -1 required by getline)
139 * @note Flaw related to getline: Errors from sub-functions are ignored
140 * @see cli_qmark_hook
141 */
142 static int
cli_tab_hook(cligen_handle h,int * cursorp)143 cli_tab_hook(cligen_handle h,
144 int *cursorp)
145 {
146 int retval = -1;
147 int old_cursor;
148 parse_tree *pt = NULL; /* Orig */
149 parse_tree *ptn = NULL; /* Expanded */
150 cvec *cvv = NULL;
151
152 if ((ptn = pt_new()) == NULL)
153 goto done;
154 old_cursor = *cursorp; /* Save old location of cursor */
155 if ((pt = cligen_ph_active_get(h)) == NULL)
156 goto ok;
157 if (pt_expand_treeref(h, NULL, pt) < 0) /* sub-tree expansion */
158 goto done;
159 if ((cvv = cvec_start(cligen_buf(h))) == NULL)
160 goto done;
161 if (pt_expand(h, pt, cvv, 1, 0, ptn) < 0) /* expansion */
162 goto done;
163 /* Note, can change cligen buf pointer (append and increase) */
164 if (cli_complete(h, cursorp, ptn, cvv) < 0) /* XXX expand-cleanup must be done here before show commands */
165 goto done;
166 else {
167 if (old_cursor == *cursorp) { /* Cursor hasnt changed */
168 fputs("\n", stdout);
169 if (cligen_tabmode(h)&CLIGEN_TABMODE_COLUMNS){
170 if (show_help_line(h, stdout, cligen_buf(h), ptn, cvv) < 0)
171 goto done;
172 }
173 else{
174 if (show_help_columns(h, stdout, cligen_buf(h), ptn, cvv) < 0)
175 goto done;
176 }
177 }
178 }
179 ok:
180 retval = 0;
181 done:
182 if (cvv)
183 cvec_free(cvv);
184 if (ptn && pt_free(ptn, 0) < 0)
185 return -1;
186 if (pt != NULL) {
187 if (pt_expand_cleanup(pt) < 0)
188 return -1;
189 if (pt_expand_treeref_cleanup(pt) < 0)
190 return -1;
191 }
192 return retval;
193 }
194
195 /*! Initialize this module
196 */
197 void
cliread_init(cligen_handle h)198 cliread_init(cligen_handle h)
199 {
200 gl_qmark_hook = cli_qmark_hook;
201 gl_tab_hook = cli_tab_hook; /* XXX globals */
202 }
203
204 /*! Print columns
205 * @param[in] cnr Number of columns.
206 * @param[in] cw Width of column
207 */
208 static int
column_print(FILE * fout,int cnr,int cw,struct cmd_help * chvec,int len,int level)209 column_print(FILE *fout,
210 int cnr,
211 int cw,
212 struct cmd_help *chvec,
213 int len,
214 int level)
215 {
216 int retval = -1;
217 int li; /* line number */
218 int ci; /* column number */
219 int linenr;
220 struct cmd_help *ch;
221
222 linenr = (len-1)/cnr + 1;
223 for (ci=0, li = 0; li < linenr; li++) {
224 while ((ci < cnr) && (li*cnr+ci < len)) {
225 ch = &chvec[li*cnr+ci];
226 fprintf(fout, " %*s",
227 -(cw-1),
228 ch->ch_cmd);
229 ci++;
230 }
231 ci = 0;
232 fprintf(fout, "\n");
233 }
234 fflush(fout);
235 retval = 0;
236 // done:
237 return retval;
238 }
239
240 /*! Show briefly the commands available (show no help)
241 * Typically called when TAB is pressed and there are multiple options.
242 * @param[in] fout This is where the output (help text) is shown.
243 * @param[in] string Input string to match
244 * @param[in] pt Vector of commands (array of cligen object pointers (cg_obj)
245 * @param[out] cvv Cligen variable vector containing vars/values pair for completion
246 * @retval 0 OK
247 * @retval -1 Error
248 */
249 static int
show_help_columns(cligen_handle h,FILE * fout,char * string,parse_tree * pt,cvec * cvv)250 show_help_columns(cligen_handle h,
251 FILE *fout,
252 char *string,
253 parse_tree *pt,
254 cvec *cvv)
255 {
256 int retval = -1;
257 int level;
258 int matchlen = 0;
259 int *matchvec = NULL;
260 int vi;
261 int i;
262 int nrcmd = 0;
263 struct cmd_help *chvec = NULL;
264 struct cmd_help *ch;
265 cg_obj *co;
266 cbuf *cb = NULL;
267 char *cmd;
268 char *prev = NULL;
269 int maxlen = 0;
270 int column_width;
271 int column_nr;
272 int rest;
273 cvec *cvt = NULL; /* Tokenized string: vector of tokens */
274 cvec *cvr = NULL; /* Rest variant, eg remaining string in each step */
275 parse_tree *ptmatch = NULL;
276
277 if (string == NULL){
278 errno = EINVAL;
279 goto done;
280 }
281 if ((cb = cbuf_new()) == NULL){
282 fprintf(stderr, "cbuf_new: %s\n", strerror(errno));
283 return -1;
284 }
285 /* Tokenize the string and transform it into two CLIgen vectors: tokens and rests */
286 if (cligen_str2cvv(string, &cvt, &cvr) < 0)
287 goto done;
288 if (match_pattern(h, cvt, cvr,
289 pt,
290 0, /* best: Return all options, not only best */
291 1, 1,
292 &ptmatch,
293 &matchvec, &matchlen,
294 cvv, NULL,
295 NULL) < 0)
296 goto done;
297 if ((level = cligen_cvv_levels(cvt)) < 0)
298 goto done;
299 if (matchlen > 0){ /* min, max only defined if matchlen > 0 */
300 /* Go through match vector and collect commands and helps */
301 if ((chvec = calloc(matchlen, sizeof(struct cmd_help))) ==NULL){
302 fprintf(stderr, "%s calloc: %s\n", __FUNCTION__, strerror(errno));
303 goto done;
304 }
305 nrcmd = 0;
306 for (i = 0; i<matchlen; i++){ // nr-1?
307 vi=matchvec[i];
308 if ((co = pt_vec_i_get(ptmatch, vi)) == NULL)
309 continue;
310 if (co->co_command == NULL)
311 continue;
312 cmd = NULL;
313 switch (co->co_type){
314 case CO_VARIABLE:
315 cbuf_reset(cb);
316 cov2cbuf(cb, co, 1);
317 cmd = cbuf_get(cb);
318 break;
319 case CO_COMMAND:
320 cmd = co->co_command;
321 break;
322 case CO_REFERENCE:
323 default:
324 continue;
325 }
326 if (cmd == NULL || strlen(cmd)==0)
327 continue;
328 if (prev && strcmp(cmd, prev)==0)
329 continue;
330 ch = &chvec[nrcmd++];
331 if ((ch->ch_cmd = strdup(cmd)) == NULL){
332 fprintf(stderr, "%s strdup: %s\n", __FUNCTION__, strerror(errno));
333 goto done;
334 }
335 prev = ch->ch_cmd;
336 maxlen = strlen(cmd)>maxlen?strlen(cmd):maxlen;
337 }
338 maxlen++;
339 column_width = maxlen<COLUMN_MIN_WIDTH?COLUMN_MIN_WIDTH:maxlen;
340 column_nr = cligen_terminal_width(h)/column_width;
341 if (column_nr < 1)
342 column_nr = 1;
343 rest = cligen_terminal_width(h)%column_width;
344 column_width += rest/column_nr;
345 if (column_print(fout,
346 column_nr,
347 column_width,
348 chvec,
349 nrcmd,
350 level) < 0)
351 goto done;
352 } /* nr>0 */
353
354 retval = 0;
355 done:
356 if (chvec){
357 for (i=0; i<nrcmd; i++){
358 if (chvec[i].ch_cmd)
359 free(chvec[i].ch_cmd);
360 }
361 free(chvec);
362 }
363 if (ptmatch && ptmatch != pt)
364 pt_free(ptmatch, 0);
365 if (cvt)
366 cvec_free(cvt);
367 if (cvr)
368 cvec_free(cvr);
369 if (cb)
370 cbuf_free(cb);
371 if (matchvec)
372 free(matchvec);
373 return retval;
374 }
375
376 /*! Show one row per command with help text for each command
377 * Typically called when a question mark is pressed
378 * @param[in] h cligen handle
379 * @param[in] fout This is where the output (help text) is shown.
380 * @param[in] string Input string to match
381 * @param[in] pt Parse tree
382 * @param[out] cvv Cligen variable vector containing vars/values pair for completion
383 * @retval 0 OK
384 * @retval -1 Error
385 *
386 * Example from JunOS
387 # set interfaces ?
388 Possible completions:
389 <interface_name> Interface name
390 + apply-groups Groups from which to inherit configuration data
391 + apply-groups-except Don't inherit configuration data from these groups
392 fe-0/0/1 Interface name
393 > interface-set Logical interface set configuration
394 > traceoptions Interface trace options
395
396 */
397 static int
show_help_line(cligen_handle h,FILE * fout,char * string,parse_tree * pt,cvec * cvv)398 show_help_line(cligen_handle h,
399 FILE *fout,
400 char *string,
401 parse_tree *pt,
402 cvec *cvv)
403 {
404 int retval = -1;
405 int level;
406 int matchlen = 0;
407 int *matchvec = NULL;
408 cvec *cvt = NULL; /* Tokenized string: vector of tokens */
409 cvec *cvr = NULL; /* Rest variant, eg remaining string in each step */
410 cg_var *cvlastt; /* Last element in cvt */
411 cg_var *cvlastr; /* Last element in cvr */
412 cligen_result result;
413 parse_tree *ptmatch = NULL;
414
415 if (string == NULL){
416 errno = EINVAL;
417 goto done;
418 }
419 /* Tokenize the string and transform it into two CLIgen vectors: tokens and rests */
420 if (cligen_str2cvv(string, &cvt, &cvr) < 0) /* XXX cvr leaks memory */
421 goto done;
422 if (match_pattern(h,
423 cvt, cvr, /* token string */
424 pt, /* command vector */
425 0, /* best: Return all options, not only best */
426 1, 1,
427 &ptmatch,
428 &matchvec, &matchlen,
429 cvv, NULL,
430 NULL) < 0)
431 goto done;
432 if (matchlen) /* sanity */
433 assert(matchvec!= NULL && ptmatch != NULL);
434 if ((level = cligen_cvv_levels(cvt)) < 0)
435 goto done;
436
437 /* If last char is blank, look for next level in parse-tree
438 * eg, syntax is x (y|z) and we have typed 'x ' then show
439 * help for y and z, not x.
440 */
441 /* See if string is not empty and last element is empty/blank
442 * This means we need to peek in next level and if that provides a unique solution,
443 * then add a <cr>
444 */
445 cvlastt = cvec_i(cvt, cvec_len(cvt)-1);
446 if (cvec_len(cvt) > 2 && strcmp(cv_string_get(cvlastt), "")==0){
447 /* if it is ok to <cr> here (at end of one mode)
448 Example: x [y|z] and we have typed 'x ', then show
449 help for y and z and a 'cr' for 'x'.
450 */
451
452 /* Remove the last elements. Awkward: first free the cv then truncate the cvec */
453 if (cvlastt)
454 cv_reset(cvlastt);
455 cvec_del_i(cvt, cvec_len(cvt)-1); /* We really just want to truncate len-1 */
456
457 if ((cvlastr = cvec_i(cvr, cvec_len(cvr)-1)) != NULL)
458 cv_reset(cvlastr);
459 cvec_del_i(cvr, cvec_len(cvr)-1);
460
461 if (match_pattern_exact(h, cvt, cvr, pt,
462 1, cvv, NULL,
463 NULL, NULL,
464 &result, NULL) < 0)
465 goto done;
466 if (result == CG_MATCH || result == CG_MULTIPLE){
467 fprintf(fout, " <cr>\n");
468 fflush(fout);
469 }
470 }
471 if (matchlen == 0){
472 retval = 0;
473 goto done;
474 }
475 /* ptmatch points to expanded nodes from first match_pattern call */
476 if (print_help_lines(h, fout, ptmatch, matchvec, matchlen) < 0)
477 goto done;
478
479 retval = 0;
480 done:
481 if (ptmatch && pt != ptmatch)
482 pt_free(ptmatch, 0);
483 if (cvt)
484 cvec_free(cvt);
485 if (cvr)
486 cvec_free(cvr);
487 if (matchvec)
488 free(matchvec);
489 return retval;
490 }
491
492 /*! Try to complete a command as much as possible.
493 * @param[in] h CLIgen handle
494 * @param[in] string Input string to match
495 * @param[in] cursorp Pointer to the current cursor in string.
496 * @param[in] pt Vector of commands (array of cligen object pointers)
497 * @param[out] cvv cligen variable vector containing vars/values pair for completion
498 * @retval -1 Error
499 * @retval 0 Success
500 */
501 static int
cli_complete(cligen_handle h,int * cursorp,parse_tree * pt,cvec * cvv)502 cli_complete(cligen_handle h,
503 int *cursorp,
504 parse_tree *pt,
505 cvec *cvv)
506 {
507 int retval = -1;
508 char *string;
509 char *s = NULL;
510 size_t slen;
511 int cursor = *cursorp;
512 int i, n;
513 int extra;
514
515 string = cligen_buf(h);
516 if (string == NULL){
517 fprintf(stderr, "%s Input string NULL\n", __FUNCTION__);
518 goto done;
519 }
520 slen = cligen_buf_size(h);
521 if ((s = malloc(slen)) == NULL){ /* s is a temporary copy */
522 fprintf(stderr, "%s malloc: %s\n", __FUNCTION__, strerror(errno));
523 goto done;
524 }
525 strncpy(s, string, slen);
526 s[cursor] = '\0';
527 if (match_complete(h, pt, &s, &slen, cvv) < 0)
528 goto done;
529 extra = strlen(s) - cursor; /* Extra characters added? */
530 if (extra){
531 cligen_buf_increase(h, strlen(s));
532 string = cligen_buf(h);
533 n = strlen(string) - cursor; /* Nr of chars right of cursor to copy */
534 cligen_buf_increase(h, strlen(string)+cursor+n);
535 string = cligen_buf(h);
536 for (i=cursor+n; i>=cursor; i--) /* Copy right of cursor */
537 string[i + extra] = string[i];
538 strncpy(string + cursor, s + cursor, extra); /* Copy the new stuff */
539 *cursorp += extra; /* Increase cursor */
540 }
541 retval = 0;
542 done:
543 if (s)
544 free(s);
545 return retval;
546 }
547
548 /*! Trim command line. Remove any leading, trailing and multiple whitespace
549 * comment is a character (eg '#')
550 * @param[out] line
551 * @param[in] comment
552 */
553 void
cli_trim(char ** line,char comment)554 cli_trim(char **line,
555 char comment)
556 {
557 int point;
558 int whitespace = 0;
559 char *s = *line;
560 char *s1 = s;
561
562 if (!isascii(comment))
563 comment = 0;
564 for (point = 0; point <= strlen(s) ; point++) {
565 if (comment && s[point] == comment){
566 *s1++ = '\n';
567 *s1++ = '\0';
568 break;
569 }
570 else
571 if (isblank(s[point])) {
572 if (whitespace)
573 continue;
574 else {
575 whitespace = 1;
576 *s1++ = ' ';
577 }
578 } else {
579 whitespace = 0;
580 *s1++ = s[point];
581 }
582 }
583
584 /* strip heading whites */
585 while ((strlen(s) > 0) && isblank(*s))
586 s++;
587
588 /* strip trailing whites and newlines */
589 while ((strlen(s) > 0) && (isblank(*(s+strlen(s)-1)) || *(s+strlen(s)-1) == '\n'))
590 *(s + strlen(s) - 1) = '\0';
591
592
593 *line = s;
594 }
595
596 /*! Given an input string, return a parse-tree.
597 *
598 * Given an input string and a parse-tree, return a matching parse-tree node, a
599 * CLIgen keyword and CLIgen variable record vector.
600 * Some complexity in this function is due to variable expansion: if there
601 * are <expand:> variables, the parse-tree needs to be expanded with current
602 * values by calling user-supplied callbacks and building a 'shadow' parse-tree
603 * which is purged after use.
604 * Use this function if you already have a string but you want it syntax-checked
605 * and parsed.
606 *
607 * @param[in] h Cligen handle
608 * @param[in] string Input string to match
609 * @param[in] pt Parse-tree
610 * @param[out] co_orig Object that matches (if retval == 1).
611 * @param[out] cvv Variable vector (if retval == 1).
612 * @param[out] result Result, < 0: errors, >=0 number of matches
613 * @param[out] reason Error reason if result is nomatch. Need to be free:d
614 * @retval 0 OK
615 * @retval -1 Error
616 *
617 * cvv should be created but empty on entry
618 * On exit it contains the command string as 0th element, and one entry per element
619 * Example: "aa <bb:str>" and inut string "aa 22" gives:
620 * 0 : "aa 22" # initial command has no "name"
621 * 1 : aa = "aa" # string has keyword itself as value
622 * 2 : bb = 22 # variable
623 */
624 int
cliread_parse(cligen_handle h,char * string,parse_tree * pt,cg_obj ** co_orig,cvec * cvvall,cligen_result * result,char ** reason)625 cliread_parse(cligen_handle h,
626 char *string,
627 parse_tree *pt, /* Orig */
628 cg_obj **co_orig,
629 cvec *cvvall,
630 cligen_result *result,
631 char **reason)
632 {
633 int retval = -1;
634 cg_obj *match_obj;
635 parse_tree *ptn = NULL; /* Expanded */
636 parse_tree *ptmatch = NULL;
637 cvec *cvt = NULL; /* Tokenized string: vector of tokens */
638 cvec *cvr = NULL; /* Rest variant, eg remaining string in each step */
639 cg_var *cv;
640 cvec *cvv = NULL; /* Top-level vars/val vector with just command as 0th element */
641
642 if (cvvall == NULL || cvec_len(cvvall) != 0){
643 errno = EINVAL;
644 goto done;
645 }
646 if ((ptn = pt_new()) == NULL)
647 goto done;
648 if (cligen_logsyntax(h) > 0){
649 fprintf(stderr, "%s:\n", __FUNCTION__);
650 pt_print(stderr, pt, 0);
651 }
652 cli_trim(&string, cligen_comment(h));
653 /* Tokenize the string and transform it into two CLIgen vectors: tokens and rests */
654 if (cligen_str2cvv(string, &cvt, &cvr) < 0)
655 goto done;
656 if (pt_expand_treeref(h, NULL, pt) < 0) /* sub-tree expansion, ie @ */
657 goto done;
658 if ((cv = cvec_add(cvvall, CGV_REST)) == NULL)
659 goto done;
660 cv_name_set(cv, "cmd"); /* the whole command string */
661 cv_string_set(cv, string); /* the whole command string */
662 if ((cvv = cvec_start(string)) == NULL)
663 goto done;
664 if (pt_expand(h, pt, cvv, 0, 0, ptn) < 0) /* sub-tree expansion, ie choice, expand function */
665 goto done;
666 if (match_pattern_exact(h, cvt, cvr,
667 ptn, 0, cvv, cvvall,
668 &match_obj, &ptmatch,
669 result, reason) < 0)
670 goto done;
671 /* Map from ghost object match_obj to real object */
672 if (match_obj && match_obj->co_ref)
673 *co_orig = match_obj->co_ref;
674 else
675 *co_orig = match_obj;
676 retval = 0;
677 done:
678 if (cvv)
679 cvec_free(cvv);
680 if (cvt)
681 cvec_free(cvt);
682 if (cvr)
683 cvec_free(cvr);
684 if (ptmatch && ptmatch != ptn)
685 if (pt_free(ptmatch, 0) < 0)
686 return -1;
687 if (ptn)
688 if (pt_free(ptn, 0) < 0)
689 return -1;
690
691 if (pt_expand_cleanup(pt) < 0)
692 return -1;
693 return retval;
694 }
695
696 /*! Read line interactively from terminal using getline (completion, etc)
697 *
698 * @param[in] h CLIgen handle
699 * @param[out] stringp Pointer to command buffer or NULL on EOF
700 * @retval 0 OK
701 * @retval -1 Error
702 */
703 int
cliread(cligen_handle h,char ** stringp)704 cliread(cligen_handle h,
705 char **stringp)
706 {
707 int retval = -1;
708 char *buf = NULL;
709
710 if (stringp == NULL){
711 errno = EINVAL;
712 goto done;
713 }
714 *stringp = NULL;
715 do {
716 buf = NULL;
717 if (gl_getline(h, &buf) < 0)
718 goto done;
719 cli_trim(&buf, cligen_comment(h));
720 } while (strlen(buf) == 0 && !gl_eof());
721 if (gl_eof())
722 goto eof;
723 if (hist_add(h, buf) < 0)
724 goto done;
725 *stringp = buf;
726 eof:
727 retval = 0;
728 done:
729 return retval;
730 }
731
732 #ifdef notused
733 /*
734 * cliread_getline
735 * Read line interactively from terminal using getline (completion, etc), given
736 * a parse-tree.
737 * Return the matching object and a CLIgen variable record.
738 * Use this function if you want an interactive CLI
739 *
740 * @param[in] h CLIgen handle
741 * @param[out] line Pointer to new string input from terminal
742 * @param[out] match_obj Matching object (if retval = 1)
743 * @param[out] cvv Variable vector (if retval = 1)
744 * @retval -2 EOF
745 * @retval -1 Error
746 * @retval 0 No match
747 * @retval 1 Exactly one match
748 * @retval 2+ Multiple matches
749 */
750 int
cliread_getline(cligen_handle h,char ** line,cg_obj ** match_obj,cvec * cvv)751 cliread_getline(cligen_handle h,
752 char **line,
753 cg_obj **match_obj,
754 cvec *cvv)
755 {
756 char *string;
757
758 if ((string = cliread(h)) == NULL) /* EOF */
759 return CG_EOF;
760
761 *line = string;
762 return cliread_parse(h, string, match_obj, cvv);
763 }
764 #endif /* notused */
765
766 /*! Read line from terminal, parse the string, and invoke callbacks.
767 *
768 *
769 * Return both results from parsing (function return), and eventual result
770 * from callback (if function return =1).
771 * Use this function if you want the whole enchilada without special operation
772 *
773 * @param[in] h CLIgen handle
774 * @param[out] line Pointer to new string input from terminal
775 * @param[out] cb_retval Retval of callback (only if functions return value is 1)
776 * @param[out] result Number of matches
777 * @param[out] reason Error reason if result is nomatch. Need to be free:d
778 * @retval 0 OK
779 * @retval -1 Error
780 */
781 int
cliread_eval(cligen_handle h,char ** line,int * cb_retval,cligen_result * result,char ** reason)782 cliread_eval(cligen_handle h,
783 char **line,
784 int *cb_retval,
785 cligen_result *result,
786 char **reason)
787 {
788 int retval = -1;
789 cg_obj *matchobj; /* matching syntax node */
790 cvec *cvv;
791 parse_tree *pt = NULL; /* Orig */
792
793 if (h == NULL){
794 fprintf(stderr, "Illegal cligen handle\n");
795 goto done;
796 }
797 if (cliread(h, line) < 0)
798 goto done;
799 if (*line == NULL){ /* EOF */
800 *result = CG_EOF;
801 goto ok;
802 }
803 if ((pt = cligen_ph_active_get(h)) == NULL){
804 fprintf(stderr, "No active parse-tree found\n");
805 goto done;;
806 }
807 if ((cvv = cvec_new(0)) == NULL){
808 fprintf(stderr, "%s: cvec_new: %s\n", __FUNCTION__, strerror(errno));
809 goto done;;
810 }
811 if (cliread_parse(h, *line, pt, &matchobj, cvv, result, reason) < 0)
812 goto done;
813 if (*result == CG_MATCH)
814 *cb_retval = cligen_eval(h, matchobj, cvv);
815 cvec_free(cvv);
816 ok:
817 retval = 0;
818 done:
819 /* XXX: Get parse-tree */
820 if (pt && pt_expand_treeref_cleanup(pt) < 0)
821 retval = -1;
822 return retval;
823 }
824
825 /*! Evaluate a matched CV and a cv variable list
826 *
827 * @param[in] h Application-specific pointer to a struct
828 * @param[in] co Object that has been matched. This is the object furthest down
829 * in the syntax tree. By backtracking to the top the complete path
830 * can be retreived.
831 * @param[in] cvv A vector of cligen variables present in the string.
832 *
833 * @retval int If there is a callback, the return value of the callback is returned,
834 * @retval 0 otherwise
835 *
836 * This is the only place where cligen callbacks are invoked
837 */
838 int
cligen_eval(cligen_handle h,cg_obj * co,cvec * cvv)839 cligen_eval(cligen_handle h,
840 cg_obj *co,
841 cvec *cvv)
842 {
843 struct cg_callback *cc;
844 int retval = 0;
845 cvec *argv;
846
847 if (h)
848 cligen_co_match_set(h, co);
849 for (cc = co->co_callbacks; cc; cc=cc->cc_next){
850 /* Vector cvec argument to callback */
851 if (cc->cc_fn_vec){
852 argv = cc->cc_cvec ? cvec_dup(cc->cc_cvec) : NULL;
853 cligen_fn_str_set(h, cc->cc_fn_str);
854 if ((retval = (*cc->cc_fn_vec)(
855 cligen_userhandle(h)?cligen_userhandle(h):h,
856 cvv,
857 argv)) < 0){
858 if (argv != NULL)
859 cvec_free(argv);
860 cligen_fn_str_set(h, NULL);
861 break;
862 }
863 if (argv != NULL)
864 cvec_free(argv);
865 cligen_fn_str_set(h, NULL);
866 }
867 }
868 return retval;
869 }
870
871 /*! Turn echo off */
872 void
cligen_echo_off(void)873 cligen_echo_off(void)
874 {
875 struct termios settings;
876
877 tcgetattr(STDIN_FILENO, &settings);
878 settings.c_lflag &= (~ECHO);
879 tcsetattr(STDIN_FILENO,TCSANOW,&settings);
880 return;
881 }
882
883 /*! Turn echo on */
884 void
cligen_echo_on(void)885 cligen_echo_on(void)
886 {
887 struct termios settings;
888
889 tcgetattr(0, &settings);
890 settings.c_lflag |= ECHO;
891 tcsetattr(0,TCSANOW,&settings);
892 return;
893 }
894