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