1 /*
2  * Copyright (C) 1991, 1992, 1993 by Chris Thewalt (thewalt@ce.berkeley.edu)
3  *
4  * Permission to use, copy, modify, and distribute this software
5  * for any purpose and without fee is hereby granted, provided
6  * that the above copyright notices appear in all copies and that both the
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  This software is provided "as is" without express or
9  * implied warranty.
10  *
11  * Thanks to the following people who have provided enhancements and fixes:
12  *   Ron Ueberschaer, Christoph Keller, Scott Schwartz, Steven List,
13  *   DaviD W. Sanderson, Goran Bostrom, Michael Gleason, Glenn Kasten,
14  *   Edin Hodzic, Eric J Bivona, Kai Uwe Rommel, Danny Quah, Ulrich Betzler
15  */
16 
17 #include "cligen_config.h"
18 
19 #include <stddef.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <sys/select.h>
23 #include <netinet/in.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <signal.h>
28 
29 #include "cligen_buf.h"
30 #include "cligen_cv.h"
31 #include "cligen_cvec.h"
32 #include "cligen_parsetree.h"
33 #include "cligen_pt_head.h"
34 #include "cligen_object.h"
35 #include "cligen_io.h"
36 #include "cligen_handle.h"
37 #include "cligen_history_internal.h"
38 
39 #include "cligen_getline.h" /* exported interface */
40 
41 /********************* exported variables ********************************/
42 
43 int (*gl_in_hook)() = NULL;
44 int (*gl_out_hook)() = NULL;
45 int (*gl_tab_hook)() = NULL;
46 int (*gl_qmark_hook)() = NULL;
47 int (*gl_susp_hook)() = NULL;
48 int (*gl_interrupt_hook)() = NULL;
49 
50 /******************** internal interface *********************************/
51 
52 /* begin forward declared internal functions */
53 static void     gl_init1(void);		/* prepare to edit a line */
54 static void     gl_cleanup(void);	/* to undo gl_init1 */
55 void            gl_char_init(void);	/* get ready for no echo input */
56 void            gl_char_cleanup(void);	/* undo gl_char_init */
57 static size_t 	(*gl_strlen)() = (size_t(*)())strlen;
58 					/* returns printable prompt width */
59 
60 static void     gl_addchar(cligen_handle h, int c);	/* install specified char */
61 static void     gl_del(cligen_handle h, int loc);	/* del, either left (-1) or cur (0) */
62 static inline void gl_fixup(cligen_handle h, char*,int,int);/* fixup state variables and screen */
63 static int      gl_getc(cligen_handle h);	        /* read one char from terminal */
64 static void     gl_kill(cligen_handle h, int pos);	/* delete to EOL */
65 static void     gl_kill_begin(cligen_handle h, int pos);	/* delete to BEGIN of line */
66 static void     gl_kill_word(cligen_handle h, int pos);	/* delete word */
67 static void     gl_newline(cligen_handle);	/* handle \n or \r */
68 static int      gl_puts(char *buf);	/* write a line to terminal */
69 
70 static void     gl_transpose(cligen_handle h);	/* transpose two chars */
71 static void     gl_yank(cligen_handle h);		/* yank killed text */
72 static void     gl_word(cligen_handle h, int dir);	/* move a word */
73 
74 static void     search_addchar(cligen_handle h, int c);	/* increment search string */
75 static void     search_term(cligen_handle);	/* reset with current contents */
76 static void     search_back(cligen_handle h, int new);	/* look back for current string */
77 static void     search_forw(cligen_handle h, int new);	/* look forw for current string */
78 /* end forward declared internal functions */
79 
80 /* begin global variables */
81 static int      gl_init_done = -1;	/* terminal mode flag  */
82 static int      gl_termw = 80;		/* actual terminal width */
83 static int      gl_utf8 = 0;		/* UTF-8 experimental mode */
84 static int      gl_scrolling_mode = 1;	/* Scrolling on / off */
85 static int      gl_scrollw = 27;	/* width of EOL scrolling region */
86 static int      gl_width = 0;		/* net size available for input */
87 static int      gl_extent = 0;		/* how far to redraw, 0 means all */
88 static int      gl_overwrite = 0;	/* overwrite mode */
89 static int      gl_pos, gl_cnt = 0;     /* position and size of input */
90 
91 static char     gl_intrc = 0;		/* keyboard SIGINT char (^C) */
92 static char     gl_quitc = 0;		/* keyboard SIGQUIT char (^]) */
93 static char     gl_suspc = 0;		/* keyboard SIGTSTP char (^Z) */
94 static char     gl_dsuspc = 0;		/* delayed SIGTSTP char */
95 static int      gl_search_mode = 0;	/* search mode flag */
96 
97 static int exitchars[8] = {0,}; /* 8 different exit chars should be enough */
98 static int gl_iseof = 0;
99 
100 static int   fixup_gl_shift;	/* index of first on screen character */
101 static int   fixup_off_right;	/* true if more text right of screen */
102 static int   fixup_off_left;	/* true if more text left of screen */
103 static char  fixup_last_prompt[80] = "";
104 
105 #define SEARCH_LEN 100
106 static char  search_prompt[SEARCH_LEN+2];  /* prompt includes search string */
107 static char  search_string[SEARCH_LEN];
108 static int   search_pos = 0;      /* current location in search_string */
109 static int   search_forw_flg = 0; /* search direction flag */
110 static int   search_last = 0;	  /* last match found */
111 
112 /* end global variables */
113 
114 
115 /************************ nonportable part *********************************/
116 
117 #ifdef unix
118 #ifndef __unix__
119 #define __unix__
120 #endif /* not __unix__ */
121 #endif /* unix */
122 
123 #ifdef _IBMR2
124 #ifndef __unix__
125 #define __unix__
126 #endif
127 #endif
128 
129 #ifdef __GO32__
130 #include <pc.h>
131 #undef MSDOS
132 #undef __unix__
133 #endif
134 
135 #ifdef MSDOS
136 #include <bios.h>
137 #endif
138 
139 #ifdef __unix__
140 #include <unistd.h>
141 
142 #define POSIX
143 #ifdef POSIX		/* use POSIX interface */
144 #include <termios.h>
145 struct termios  new_termios, old_termios;
146 #else /* not POSIX */
147 #include <sys/ioctl.h>
148 #ifdef M_XENIX	/* does not really use bsd terminal interface */
149 #undef TIOCSETN
150 #endif /* M_XENIX */
151 #ifdef TIOCSETN		/* use BSD interface */
152 #include <sgtty.h>
153 struct sgttyb   new_tty, old_tty;
154 struct tchars   tch;
155 struct ltchars  ltch;
156 #else			/* use SYSV interface */
157 #include <termio.h>
158 struct termio   new_termio, old_termio;
159 #endif /* TIOCSETN */
160 #endif /* POSIX */
161 #endif	/* __unix__ */
162 
163 #ifdef vms
164 #include <descrip.h>
165 #include <ttdef.h>
166 #include <iodef.h>
167 #include unixio
168 
169 static int   setbuff[2];             /* buffer to set terminal attributes */
170 static short chan = -1;              /* channel to terminal */
171 struct dsc$descriptor_s descrip;     /* VMS descriptor */
172 #endif
173 
174 void
gl_char_init(void)175 gl_char_init(void)			/* turn off input echo */
176 {
177 #ifdef __unix__
178 #ifdef POSIX
179     tcgetattr(0, &old_termios);
180     gl_intrc = old_termios.c_cc[VINTR];
181     gl_quitc = old_termios.c_cc[VQUIT];
182 #ifdef VSUSP
183     gl_suspc = old_termios.c_cc[VSUSP];
184 #endif
185 #ifdef VDSUSP
186     gl_dsuspc = old_termios.c_cc[VDSUSP];
187 #endif
188     new_termios = old_termios;
189     new_termios.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF);
190     new_termios.c_iflag |= (IGNBRK|IGNPAR);
191     new_termios.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO);
192     new_termios.c_cc[VMIN] = 1;
193     new_termios.c_cc[VTIME] = 0;
194     tcsetattr(0, TCSADRAIN, &new_termios);
195 #else				/* not POSIX */
196 #ifdef TIOCSETN			/* BSD */
197     ioctl(0, TIOCGETC, &tch);
198     ioctl(0, TIOCGLTC, &ltch);
199     gl_intrc = tch.t_intrc;
200     gl_quitc = tch.t_quitc;
201     gl_suspc = ltch.t_suspc;
202     gl_dsuspc = ltch.t_dsuspc;
203     ioctl(0, TIOCGETP, &old_tty);
204     new_tty = old_tty;
205     new_tty.sg_flags |= RAW;
206     new_tty.sg_flags &= ~ECHO;
207     ioctl(0, TIOCSETN, &new_tty);
208 #else				/* SYSV */
209     ioctl(0, TCGETA, &old_termio);
210     gl_intrc = old_termio.c_cc[VINTR];
211     gl_quitc = old_termio.c_cc[VQUIT];
212     new_termio = old_termio;
213     new_termio.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF);
214     new_termio.c_iflag |= (IGNBRK|IGNPAR);
215     new_termio.c_lflag &= ~(ICANON|ISIG|ECHO);
216     new_termio.c_cc[VMIN] = 1;
217     new_termio.c_cc[VTIME] = 0;
218     ioctl(0, TCSETA, &new_termio);
219 #endif /* TIOCSETN */
220 #endif /* POSIX */
221 #endif /* __unix__ */
222 
223 #ifdef vms
224     descrip.dsc$w_length  = strlen("tt:");
225     descrip.dsc$b_dtype   = DSC$K_DTYPE_T;
226     descrip.dsc$b_class   = DSC$K_CLASS_S;
227     descrip.dsc$a_pointer = "tt:";
228     (void)sys$assign(&descrip,&chan,0,0);
229     (void)sys$qiow(0,chan,IO$_SENSEMODE,0,0,0,setbuff,8,0,0,0,0);
230     setbuff[1] |= TT$M_NOECHO;
231     (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0);
232 #endif /* vms */
233 }
234 
235 void
gl_char_cleanup(void)236 gl_char_cleanup(void)		/* undo effects of gl_char_init */
237 {
238 #ifdef __unix__
239 #ifdef POSIX
240     tcsetattr(0, TCSADRAIN, &old_termios);
241 #else 			/* not POSIX */
242 #ifdef TIOCSETN		/* BSD */
243     ioctl(0, TIOCSETN, &old_tty);
244 #else			/* SYSV */
245     ioctl(0, TCSETA, &old_termio);
246 #endif /* TIOCSETN */
247 #endif /* POSIX */
248 #endif /* __unix__ */
249 
250 #ifdef vms
251     setbuff[1] &= ~TT$M_NOECHO;
252     (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0);
253     sys$dassgn(chan);
254     chan = -1;
255 #endif
256 }
257 
258 #if MSDOS || __EMX__ || __GO32__
pc_keymap(int c)259 int pc_keymap(int c)
260 {
261     switch (c) {
262     case 72: c = 16;   /* up -> ^P */
263         break;
264     case 80: c = 14;   /* down -> ^N */
265         break;
266     case 75: c = 2;    /* left -> ^B */
267         break;
268     case 77: c = 6;    /* right -> ^F */
269         break;
270     default: c = 0;    /* make it garbage */
271     }
272     return c;
273 }
274 #endif /* MSDOS || __EMX__ || __GO32__ */
275 
276 
277 #if CLIGEN_REGFD
278 struct regfd {
279     int fd;
280     int (*cb)(int, void*);
281     void *arg;
282 };
283 static int nextfds = 0;
284 static struct regfd *extfds = NULL;
285 
286 /* XXX: If arg is malloced, the treatment of arg creates leaks */
287 int
gl_regfd(int fd,cligen_fd_cb_t * cb,void * arg)288 gl_regfd(int fd,
289 	 cligen_fd_cb_t *cb,
290 	 void *arg)
291 {
292     int i;
293     struct regfd *tmp;
294 
295     for (i = 0; i < nextfds; i++) {
296 	if (extfds[i].fd == fd) { /* Already registered. Update arg and cb */
297 	    extfds[i].cb = cb;
298 	    extfds[i].arg = arg;
299 	    return 0;
300 	}
301     }
302 
303     if ((tmp = realloc(extfds, (nextfds+1) * sizeof(*extfds))) == NULL)
304 	return -1;
305     tmp[nextfds].fd = fd;
306     tmp[nextfds].cb = cb;
307     tmp[nextfds].arg = arg;
308     extfds = tmp;
309     nextfds++;
310 
311     return 0;
312 }
313 
314 int
gl_unregfd(int fd)315 gl_unregfd(int fd)
316 {
317     int i;
318 
319     for (i = 0; i < nextfds; i++) {
320 	if (extfds[i].fd == fd) {
321 	    if (i+1 < nextfds)
322 		memcpy(&extfds[i], &extfds[i+1], nextfds-i);
323 	    extfds = realloc(extfds, (nextfds-1) * sizeof(*extfds));
324 	    nextfds--;
325 	    return 0;
326 	}
327     }
328 
329     return -1;
330 }
331 
332 int
gl_select()333 gl_select()
334 {
335     int i;
336     fd_set fdset;
337 
338     while (1){
339 	FD_ZERO(&fdset);
340 	FD_SET(0, &fdset);
341 	for(i = 0; i < nextfds; i++)
342 	    FD_SET(extfds[i].fd, &fdset);
343 	if (select(FD_SETSIZE, &fdset, NULL, NULL, NULL) < 0)
344 	    return -1;
345 	for(i = 0; i < nextfds; i++)
346 	    if (FD_ISSET(extfds[i].fd, &fdset))
347 		if (extfds[i].cb(extfds[i].fd, extfds[i].arg) < 0)
348 		    return -1;
349 	if (FD_ISSET(0, &fdset))
350 	    break;
351     }
352     return 0;
353 }
354 #endif
355 
356 
357 int
gl_eof()358 gl_eof()
359 {
360     return gl_iseof;
361 }
362 
363 void
gl_exitchar_add(char c)364 gl_exitchar_add(char c)
365 {
366     int i;
367 
368     for (i=0;sizeof(exitchars);i++)
369 	if (!exitchars[i]){
370 	    exitchars[i] = c;
371 	    break;
372 	}
373 }
374 
375 /* check if c is an exit char */
376 static int
gl_exitchar(char c)377 gl_exitchar(char c)
378 {
379     int i;
380 
381     for (i=0;sizeof(exitchars);i++){
382 	if (!exitchars[i])
383 	    break;
384 	if (exitchars[i] == c)
385 	    return 1;
386     }
387     return 0; /* ^C */
388 }
389 
390 /*! Initiate an exit, not actually made, but gl_exiting() will return 1.
391  * @param[in]  h     CLIgen handle
392  */
393 static char *
gl_exit(cligen_handle h)394 gl_exit(cligen_handle h)
395 {
396     char *gl_buf = cligen_buf(h);
397 
398     gl_iseof++;
399     gl_buf[0] = 0;
400     gl_cleanup();
401     gl_putc('\n');
402     return gl_buf;
403 }
404 
405 /*! Get a character without echoing it to screen
406  * @param[in]  h     CLIgen handle
407  */
408 static int
gl_getc(cligen_handle h)409 gl_getc(cligen_handle h)
410 {
411     int             c;
412 #ifdef __unix__
413     unsigned char  ch;
414 #endif
415 
416 #if CLIGEN_REGFD
417     gl_select(); /* block until something arrives on stdin */
418 #endif
419 #ifdef __unix__
420     while ((c = read(0, &ch, 1)) == -1) {
421 	if (errno == EINTR){
422 	    if (gl_interrupt_hook(h) <0)
423 		return -1;
424 	    continue;
425 	}
426 	break;
427     }
428     if (c == 0){
429 	gl_iseof++;
430 	cligen_buf(h)[0] = 0; /* clean exit from gl? */
431 	gl_cleanup();
432 	gl_putc('\n');
433 	return -1;
434     }
435     c = (ch <= 0)? -1 : ch;
436 #endif	/* __unix__ */
437 #ifdef MSDOS
438     c = _bios_keybrd(_NKEYBRD_READ);
439 #endif  /* MSDOS */
440 #ifdef __GO32__
441     c = getkey() ;
442     if (c > 255) c = pc_keymap(c & 0377);
443 #endif /* __GO32__ */
444 #ifdef __TURBOC__
445     while(!bioskey(1))
446 	;
447     c = bioskey(0);
448 #endif
449 #if MSDOS || __TURBOC__
450     if ((c & 0377) == 224) {
451 	c = pc_keymap((c >> 8) & 0377);
452     } else {
453 	c &= 0377;
454     }
455 #endif /* MSDOS || __TURBOC__ */
456 #ifdef __EMX__
457     c = _read_kbd(0, 1, 0);
458     if (c == 224 || c == 0) {
459         c = pc_keymap(_read_kbd(0, 1, 0));
460     } else {
461         c &= 0377;
462     }
463 #endif
464 #ifdef vms
465     if(chan < 0) {
466        c='\0';
467     }
468     (void)sys$qiow(0,chan,IO$_TTYREADALL,0,0,0,&c,1,0,0,0,0);
469     c &= 0177;                        /* get a char */
470 #endif
471     return c;
472 }
473 
474 int
gl_putc(int c)475 gl_putc(int c)
476 {
477     char   ch = c;
478 
479     if (write(1, &ch, 1) < 0)
480       return -1;
481     if (ch == '\n') {
482 	ch = '\r'; /* RAW mode needs '\r', does not hurt */
483         if (write(1, &ch, 1) < 0)
484 	  return -1;
485     }
486     return 0;
487 }
488 
489 /******************** fairly portable part *********************************/
490 
491 static int
gl_puts(char * buf)492 gl_puts(char *buf)
493 {
494     int len;
495 
496     if (buf) {
497         len = strlen(buf);
498         if (write(1, buf, len) < 0)
499 	  return -1;
500     }
501     return 0;
502 }
503 
504 /*! set up variables and terminal
505  * @see gl_init  cal this once first
506  */
507 static void
gl_init1()508 gl_init1()
509 {
510     gl_iseof = 0;
511     gl_char_init();
512     gl_init_done = 1;
513 }
514 
515 /*! undo effects of gl_init1, as necessary */
516 static void
gl_cleanup()517 gl_cleanup()
518 {
519     if (gl_init_done > 0)
520         gl_char_cleanup();
521     gl_init_done = 0;
522 }
523 
524 int
gl_getscrolling(void)525 gl_getscrolling(void)
526 {
527     return gl_scrolling_mode;
528 }
529 
530 void
gl_setscrolling(int mode)531 gl_setscrolling(int  mode)
532 {
533     gl_scrolling_mode = mode;
534 }
535 
536 int
gl_getwidth(void)537 gl_getwidth(void)
538 {
539     return gl_termw;
540 }
541 
542 /*! Set UTF-8 experimental mode
543  * @param[in] enabled   Set to 1 to enable UTF-8 experimental mode
544  */
545 int
gl_utf8_set(int mode)546 gl_utf8_set(int mode)
547 {
548     gl_utf8 = mode;
549     return 0;
550 }
551 
552 /*! get UTF-8 experimental mode
553  * @retval 0 UTF-8 is disabled
554  * @retval 1 UTF-8 is enabled
555  */
556 int
gl_utf8_get(void)557 gl_utf8_get(void)
558 {
559     return gl_utf8;
560 }
561 
562 int
gl_setwidth(int w)563 gl_setwidth(int  w)
564 {
565     if (w < TERM_MIN_SCREEN_WIDTH)
566 	return -1;
567     gl_termw = w;
568     gl_scrollw = w / 3;
569     return 0;
570 }
571 
572 /*! Main getline function handling a command line
573  *
574  * @param[in]  h     CLIgen handle
575  * @param[out] buf   Pointer to char* buffer containing CLIgen command
576  * @retval     0     OK: string or EOF
577  * @retval    -1     Error
578  * Typically called by cliread.
579  */
580 int
gl_getline(cligen_handle h,char ** buf)581 gl_getline(cligen_handle h,
582 	   char        **buf)
583 {
584     int             c, loc, tmp;
585     char           *gl_prompt;
586     int             escape = 0;
587 #ifdef __unix__
588     int	            sig;
589 #endif
590 
591     gl_init1();
592     gl_prompt = (cligen_prompt(h))? cligen_prompt(h) : "";
593     cligen_buf(h)[0] = 0;
594     if (gl_in_hook)
595 	gl_in_hook(h, cligen_buf(h));
596     gl_fixup(h, gl_prompt, -2, cligen_buf_size(h));
597     while ((c = gl_getc(h)) >= 0) {
598 	gl_extent = 0;  	/* reset to full extent */
599 	if (isprint(c) || (escape&&c=='\n')) {
600 	    if (escape == 0 && c == '\\')
601                escape++;
602             else{
603 		if (escape ==0 && c == '?' && gl_qmark_hook) {
604 		    escape = 0;
605 		    if ((loc = gl_qmark_hook(h, cligen_buf(h))) < 0)
606 			goto err;
607 		    gl_fixup(h, gl_prompt, -2, gl_pos);
608 		}
609 		else{
610 		    escape = 0;
611 		    if (gl_search_mode)
612 			search_addchar(h, c);
613 		    else
614 			gl_addchar(h, c);
615 		}
616 	    }
617 	} else {
618 	    escape = 0;
619 	    if (gl_search_mode) { /* after ^S or ^R */
620 	        if (c == '\033' || c == '\016' || c == '\020') { /* ESC, ^N, ^P */
621 	            search_term(h);
622 	            c = 0;     		/* ignore the character */
623 		} else if (c == '\010' || c == '\177') { /* del */
624 		    search_addchar(h, -1); /* unwind search string */
625 		    c = 0;
626 		} else if (c != '\022' && c != '\023') { /* ^R ^S */
627 		    search_term(h);	/* terminate and handle char */
628 		}
629 	    }
630 	    /* special exit characters */
631 	    if (gl_exitchar(c))
632 		goto exit;
633 	    switch (c) {
634 	    case '\n': case '\r': 			/* newline */
635 		gl_newline(h);
636 		goto done;
637 		/*NOTREACHED*/
638 		break;
639 	    case '\001': gl_fixup(h, gl_prompt, -1, 0);		/* ^A */
640 		break;
641 	    case '\002': gl_fixup(h, gl_prompt, -1, gl_pos-1);	/* ^B */
642 		break;
643 	    case '\004':					/* ^D */
644 		if (gl_cnt == 0)
645 		    goto exit;
646 		else
647 		    gl_del(h, 0);
648 		break;
649 	    case '\005': gl_fixup(h, gl_prompt, -1, gl_cnt);	/* ^E */
650 		break;
651 	    case '\006': gl_fixup(h, gl_prompt, -1, gl_pos+1);	/* ^F */
652 		break;
653 	    case '\010': case '\177': gl_del(h, -1);	/* ^H and DEL */
654 		break;
655 	    case '\t':        				/* TAB */
656                 if (gl_tab_hook) {
657 		    tmp = gl_pos;
658 	            if ((loc = gl_tab_hook(h, &tmp)) < 0)
659 			goto err;
660 		    gl_fixup(h, gl_prompt, -2, tmp);
661 #if 0
662                  if (loc != -1 || tmp != gl_pos)
663                        gl_fixup(h, gl_prompt, loc, tmp);
664 #endif
665                 }
666 		break;
667 	    case '\013': gl_kill(h, gl_pos);			/* ^K */
668 		break;
669 	    case '\014':
670 		    gl_clear_screen(h);				/* ^L */
671 		break;
672 	    case '\016': 					/* ^N */
673 		hist_copy_next(h);
674                 if (gl_in_hook)
675 	            gl_in_hook(h, cligen_buf(h));
676 		gl_fixup(h, gl_prompt, 0, cligen_buf_size(h));
677 		break;
678 	    case '\017': gl_overwrite = !gl_overwrite;       	/* ^O */
679 		break;
680 	    case '\020': 					/* ^P */
681 		hist_copy_prev(h);
682                 if (gl_in_hook)
683 	            gl_in_hook(h, cligen_buf(h));
684 		gl_fixup(h, gl_prompt, 0, cligen_buf_size(h));
685 		break;
686 	    case '\022': search_back(h, 1);			/* ^R */
687 		break;
688 	    case '\023': search_forw(h, 1);			/* ^S */
689 		break;
690 	    case '\024': gl_transpose(h);			/* ^T */
691 		break;
692 	    case '\025': gl_kill_begin(h, gl_pos);		/* ^U */
693 		break;
694 	    case '\027': gl_kill_word(h, gl_pos);		/* ^W */
695 		break;
696 	    case '\031': gl_yank(h);				/* ^Y */
697 		break;
698 	    case '\032':                                      /* ^Z */
699 		if(gl_susp_hook) {
700 		    tmp = gl_pos;
701 	            loc = gl_susp_hook(cligen_userhandle(h)?cligen_userhandle(h):h,
702 				       cligen_buf(h), gl_strlen(gl_prompt), &tmp);
703 	            if (loc != -1 || tmp != gl_pos)
704 	                gl_fixup(h, gl_prompt, loc, tmp);
705 		    if (strchr (cligen_buf(h), '\n'))
706 			goto done;
707 		}
708 		break;
709 	    case '\033':	/* ansi arrow keys (ESC) */
710 		c = gl_getc(h);
711 		/* ESC-[ is normal and ESC-O is application cursor keys */
712 		if (c == '[' || c == 'O') {
713 		    switch(c = gl_getc(h)) {
714 		    case 'A':             			/* up */
715 			hist_copy_prev(h);
716                         if (gl_in_hook)
717 	                    gl_in_hook(h, cligen_buf(h));
718 		        gl_fixup(h, gl_prompt, 0, cligen_buf_size(h));
719 		        break;
720 		    case 'B':                         	/* down */
721 			hist_copy_next(h);
722                         if (gl_in_hook)
723 	                    gl_in_hook(h, cligen_buf(h));
724 		        gl_fixup(h, gl_prompt, 0, cligen_buf_size(h));
725 		        break;
726 		    case 'C': gl_fixup(h, gl_prompt, -1, gl_pos+1); /* right */
727 		        break;
728 		    case 'D': gl_fixup(h, gl_prompt, -1, gl_pos-1); /* left */
729 		        break;
730 		    default: gl_putc('\007');         /* who knows */
731 		        break;
732 		    }
733 		} else if (c == 'f' || c == 'F') {
734 		    gl_word(h, 1);
735 		} else if (c == 'b' || c == 'B') {
736 		    gl_word(h, -1);
737 		} else
738 		    gl_putc('\007');
739 		break;
740 	    default:		/* check for a terminal signal */
741 #ifdef __unix__
742 		if ((c & 0xe0) == 0xc0){ /* UTF-2 */
743 		    int c2;
744 		    c2 = gl_getc(h);
745 		    if (gl_utf8){
746 			gl_addchar(h, c);
747 			gl_addchar(h, c2);
748 		    }
749 		}
750 		else if ((c & 0xf0) == 0xe0){ /* UTF-2 */
751 		    int c2, c3;
752 		    c2 = gl_getc(h);
753 		    c3 = gl_getc(h);
754 		    if (gl_utf8){
755 			gl_addchar(h, c);
756 			gl_addchar(h, c2);
757 			gl_addchar(h, c3);
758 		    }
759 		}
760 		else if ((c & 0xf8) == 0xf0){ /* UTF-3 */
761 		    int c2, c3, c4;
762 		    c2 = gl_getc(h);
763 		    c3 = gl_getc(h);
764 		    c4 = gl_getc(h);
765 		    if (gl_utf8){
766 			gl_addchar(h, c);
767 			gl_addchar(h, c2);
768 			gl_addchar(h, c3);
769 			gl_addchar(h, c4);
770 		    }
771 		}
772 	        if (c > 0) {	/* ignore 0 (reset above) */
773 	            sig = 0;
774 #ifdef SIGINT
775 	            if (c == gl_intrc)
776 	                sig = SIGINT;
777 #endif
778 #ifdef SIGQUIT
779 	            if (c == gl_quitc)
780 	                sig = SIGQUIT;
781 #endif
782 #ifdef SIGTSTP
783 	            if (c == gl_suspc || c == gl_dsuspc)
784 	                sig = SIGTSTP;
785 #endif
786                     if (sig != 0) {
787 	                gl_cleanup();
788 	                kill(0, sig);
789 	                gl_init1();
790 	                gl_redraw(h);
791 			gl_kill(h, 0);
792 			c = 0;
793 		    }
794 		}
795 #endif /* __unix__ */
796                 if (c > 0)
797 		    gl_putc('\007');
798 		break;
799 	    }
800 	}
801     } /* while */
802     cligen_buf(h)[0] = 0;
803  done:
804     gl_cleanup();
805     *buf = cligen_buf(h);
806     return 0;
807  exit: /* ie exit from cli, not necessarily error */
808     gl_exit(h);
809     *buf = cligen_buf(h);
810     return 0;
811  err: /* fatal error */
812     gl_cleanup();
813     return -1;
814 }
815 
816 /*! Add the character c to the input buffer at current location
817  * @param[in]  h     CLIgen handle
818  * @param[in]  c     Character
819  */
820 static void
gl_addchar(cligen_handle h,int c)821 gl_addchar(cligen_handle h,
822 	   int           c)
823 {
824     int  i;
825 
826     cligen_buf_increase(h, gl_cnt+1); /* assume increase enough for gl_pos-gl_cnt */
827     if (gl_overwrite == 0 || gl_pos == gl_cnt) {
828         for (i=gl_cnt; i >= gl_pos; i--)
829             cligen_buf(h)[i+1] = cligen_buf(h)[i];
830         cligen_buf(h)[gl_pos] = c;
831         gl_fixup(h, cligen_prompt(h), gl_pos, gl_pos+1);
832     } else {
833 	cligen_buf(h)[gl_pos] = c;
834 	gl_extent = 1;
835         gl_fixup(h, cligen_prompt(h), gl_pos, gl_pos+1);
836     }
837 }
838 
839 /*! Add the kill buffer to the input buffer at current location
840  * @param[in]  h     CLIgen handle
841  */
842 static void
gl_yank(cligen_handle h)843 gl_yank(cligen_handle h)
844 {
845     int  i, len;
846 
847     len = strlen(cligen_killbuf(h));
848     if (len > 0) {
849 	if (gl_overwrite == 0) {
850 	    cligen_buf_increase(h, gl_cnt + len + 1);
851             for (i=gl_cnt; i >= gl_pos; i--)
852                 cligen_buf(h)[i+len] = cligen_buf(h)[i];
853 	    for (i=0; i < len; i++)
854                 cligen_buf(h)[gl_pos+i] = cligen_killbuf(h)[i];
855             gl_fixup(h, cligen_prompt(h), gl_pos, gl_pos+len);
856 	} else {
857 	    if (gl_pos + len > gl_cnt) {
858 	        cligen_buf_increase(h, gl_pos + len + 1);
859 		cligen_buf(h)[gl_pos + len] = 0;
860             }
861 	    for (i=0; i < len; i++)
862                 cligen_buf(h)[gl_pos+i] = cligen_killbuf(h)[i];
863 	    gl_extent = len;
864             gl_fixup(h, cligen_prompt(h), gl_pos, gl_pos+len);
865 	}
866     } else
867 	gl_putc('\007');
868 }
869 
870 /*! Switch character under cursor and to left of cursor
871  * @param[in]  h     CLIgen handle
872  */
873 static void
gl_transpose(cligen_handle h)874 gl_transpose(cligen_handle h)
875 {
876     int    c;
877 
878     if (gl_pos > 0 && gl_cnt > gl_pos) {
879 	c = cligen_buf(h)[gl_pos-1];
880 	cligen_buf(h)[gl_pos-1] = cligen_buf(h)[gl_pos];
881 	cligen_buf(h)[gl_pos] = c;
882 	gl_extent = 2;
883 	gl_fixup(h, cligen_prompt(h), gl_pos-1, gl_pos);
884     } else
885 	gl_putc('\007');
886 }
887 
888 /*! Cleans up entire line before returning to caller.
889  *
890  * @param[in]  h     CLIgen handle
891  * A \n is appended. If line longer than screen, redraw starting at beginning
892  */
893 static void
gl_newline(cligen_handle h)894 gl_newline(cligen_handle h)
895 {
896     int len = gl_cnt;
897     int loc;
898 
899     if (gl_scrolling_mode)
900 	loc = gl_width - 5;	/* shifts line back to start position */
901     else
902 	loc = gl_cnt;
903 
904     cligen_buf_increase(h, gl_cnt+1); /* \n\0 added */
905     if (gl_out_hook) {
906         len = strlen(cligen_buf(h));
907     }
908     if (loc > len)
909 	loc = len;
910     gl_fixup(h, cligen_prompt(h), -1, loc);	/* must do this before appending \n */
911     cligen_buf(h)[len] = '\n';
912     cligen_buf(h)[len+1] = '\0';
913     gl_putc('\n');
914 }
915 
916 /*! Delete a character.
917  * @param[in] h    CLIgen handle
918  * @param[in] loc  -1 : delete character to left of cursor
919  *                 0 : delete character under cursor
920  */
921 static void
gl_del(cligen_handle h,int loc)922 gl_del(cligen_handle h,
923        int           loc)
924 {
925     int i;
926 
927     if ((loc == -1 && gl_pos > 0) || (loc == 0 && gl_pos < gl_cnt)) {
928         for (i=gl_pos+loc; i < gl_cnt; i++)
929 	    cligen_buf(h)[i] = cligen_buf(h)[i+1];
930 	gl_fixup(h, cligen_prompt(h), gl_pos+loc, gl_pos+loc);
931     } else
932 	gl_putc('\007');
933 }
934 
935 /*! Delete position to end of line
936  * @param[in]  h     CLIgen handle
937  * @param[in]  pos   Delete from pos to the end of line
938  */
939 static void
gl_kill(cligen_handle h,int pos)940 gl_kill(cligen_handle h,
941 	int           pos)
942 {
943     if (pos < gl_cnt) {
944         cligen_killbuf_increase(h, cligen_buf_size(h));
945 	strncpy(cligen_killbuf(h), cligen_buf(h) + pos, cligen_buf_size(h));
946 	cligen_buf(h)[pos] = '\0';
947 	gl_fixup(h, cligen_prompt(h), pos, pos);
948     } else
949 	gl_putc('\007');
950 }
951 
952 /* Delete from pos to start of line
953  * @param[in]  h     CLIgen handle
954  * @param[in]  pos   Delete from pos to start of line
955  */
956 static void
gl_kill_begin(cligen_handle h,int pos)957 gl_kill_begin(cligen_handle h,
958 	      int           pos)
959 {
960     int i;
961     int len;
962 
963     if (pos != 0) {
964 	len = strlen(cligen_buf(h));
965 	cligen_killbuf_increase(h, pos);
966 	strncpy(cligen_killbuf(h), cligen_buf(h), pos);
967 	cligen_killbuf(h)[pos] = '\0';
968 	memmove(cligen_buf(h), cligen_buf(h) + pos, len-pos+1); /* memmove may overlap */
969 	gl_fixup(h, cligen_prompt(h), 0, 0);
970 	for (i=gl_pos; i < gl_cnt; i++)
971             gl_putc(cligen_buf(h)[i]);
972 	gl_fixup(h, cligen_prompt(h), -2, 0);
973     } else
974 	gl_putc('\007');
975 }
976 
977 /*! Delete one previous word from pos
978  * @param[in]  h     CLIgen handle
979  * @param[in]  pos   Delete one previous word from pos
980  */
981 static void
gl_kill_word(cligen_handle h,int pos)982 gl_kill_word(cligen_handle h,
983 	     int           pos)
984 {
985     int i, wpos;
986 
987     if (pos == 0)
988 	gl_putc('\007');
989     else {
990 	wpos = pos;
991 	if (pos > 0)
992 	    pos--;
993 	while (isspace((int)cligen_buf(h)[pos]) && pos > 0)
994 	    pos--;
995         while (!isspace((int)cligen_buf(h)[pos]) && pos > 0)
996 	    pos--;
997 	if (pos < gl_cnt && isspace((int)cligen_buf(h)[pos]))   /* move onto word */
998 	    pos++;
999 	cligen_killbuf_increase(h, wpos-pos);
1000 	strncpy(cligen_killbuf(h), cligen_buf(h)+pos, wpos-pos);
1001 	cligen_killbuf(h)[wpos-pos] = '\0';
1002 	memmove(cligen_buf(h)+pos, cligen_buf(h) + wpos, wpos-pos+1);
1003 	gl_fixup(h, cligen_prompt(h), wpos, pos);
1004 	for (i=gl_pos; i < gl_cnt; i++)
1005             gl_putc(cligen_buf(h)[i]);
1006 	gl_fixup(h, cligen_prompt(h), -2, pos);
1007 
1008     }
1009 }
1010 
1011 
1012 /*! Move forward or backword one word
1013  * @param[in]  h          CLIgen handle
1014  * @param[in]  direction  >0 forward; else backward
1015  */
1016 static void
gl_word(cligen_handle h,int direction)1017 gl_word(cligen_handle h,
1018 	int           direction)
1019 
1020 {
1021     int pos = gl_pos;
1022 
1023     if (direction > 0) {		/* forward */
1024         while (!isspace((int)cligen_buf(h)[pos]) && (pos < gl_cnt))
1025 	    pos++;
1026 	while (isspace((int)cligen_buf(h)[pos]) && pos < gl_cnt)
1027 	    pos++;
1028     } else {				/* backword */
1029 	if (pos > 0)
1030 	    pos--;
1031 	while (isspace((int)cligen_buf(h)[pos]) && pos > 0)
1032 	    pos--;
1033         while (!isspace((int)cligen_buf(h)[pos]) && pos > 0)
1034 	    pos--;
1035 	if (pos < gl_cnt && isspace((int)cligen_buf(h)[pos]))   /* move onto word */
1036 	    pos++;
1037     }
1038     gl_fixup(h, cligen_prompt(h), -1, pos);
1039 }
1040 
1041 static int
move_cursor_up(int nr)1042 move_cursor_up(int nr)
1043 {
1044     gl_putc(033);
1045     gl_putc('[');
1046     gl_putc('1');
1047     gl_putc('A');
1048     return 0;
1049 }
1050 
1051 static int
move_cursor_right(int nr)1052 move_cursor_right(int nr)
1053 {
1054     char str[16];
1055     int  i;
1056     gl_putc(033);
1057     gl_putc('[');
1058     snprintf(str, 15, "%d", nr);
1059     for (i=0; i<strlen(str); i++)
1060 	gl_putc(str[i]);
1061     gl_putc('C');
1062     return 0;
1063 }
1064 
1065 static int
wrap_line()1066 wrap_line()
1067 {
1068     gl_putc('\n'); /* wrap line */
1069     return 0;
1070 }
1071 
1072 static int
unwrap_line()1073 unwrap_line()
1074 {
1075     move_cursor_up(1);
1076     move_cursor_right(gl_termw-1);
1077     return 0;
1078 }
1079 
1080 int
wrap(int p,int plen)1081 wrap(int p,
1082      int plen)
1083 {
1084     return (p+plen+1)%gl_termw==0;
1085 }
1086 
gl_clear_screen(cligen_handle h)1087 void gl_clear_screen(cligen_handle h)
1088 {
1089     if (gl_init_done <= 0) {
1090 	return;
1091     }
1092 
1093     gl_putc('\033');	/* clear */
1094     gl_putc('[');
1095     gl_putc('2');
1096     gl_putc('J');
1097 
1098     gl_putc('\033');	/* home */
1099     gl_putc('[');
1100     gl_putc('H');
1101 
1102     gl_fixup(h, cligen_prompt(h), -2, gl_pos);
1103 }
1104 
1105 /*! Emit a newline, reset and redraw prompt and current input line
1106  * @param[in]  h     CLIgen handle
1107 */
1108 void
gl_redraw(cligen_handle h)1109 gl_redraw(cligen_handle h)
1110 {
1111     if (gl_init_done > 0) {
1112         gl_putc('\n');
1113         gl_fixup(h, cligen_prompt(h), -2, gl_pos);
1114     }
1115 }
1116 
1117 /*! Redrawing or moving within line
1118  *
1119  * This function is used both for redrawing when input changes or for
1120  * moving within the input line.  The parameters are:
1121  *
1122  * @param[in] h       CLIgen handle
1123  * @param[in] prompt  Compared to last_prompt[] for changes;
1124  * @param[in] change  Index of the start of changes in the input buffer,
1125  *                    with -1 indicating no changes, -2 indicating we're on
1126  *                    a new line, redraw everything.
1127  * @param[in] cursor  The desired location of the cursor after the call.
1128  *                    A value of cligen_buf_size(h) can be used  to indicate
1129  *                    the cursor should move just past the end of the input line.
1130  */
1131 static void
gl_fixup_noscroll(cligen_handle h,char * prompt,int change,int cursor)1132 gl_fixup_noscroll(cligen_handle h,
1133 		  char         *prompt,
1134 		  int           change,
1135 		  int           cursor)
1136 {
1137     int          left = 0, right = -1;		/* bounds for redraw */
1138     int          pad;		/* how much to erase at end of line */
1139     int          backup;        /* how far to backup before fixing */
1140     int          i;
1141     int          p; /* pos */
1142     int          new_right = -1; /* alternate right bound, using gl_extent */
1143     int          l1, l2;
1144     int          plen=strlen(prompt);
1145 
1146     if (change == -2) {   /* reset */
1147 	gl_pos = gl_cnt = fixup_gl_shift = fixup_off_right = fixup_off_left = 0;
1148 	gl_putc('\r');
1149 	gl_puts(prompt);
1150 	strncpy(fixup_last_prompt, prompt, sizeof(fixup_last_prompt));
1151 	change = 0;
1152         gl_width = gl_termw - gl_strlen(prompt);
1153     } else if (strcmp(prompt, fixup_last_prompt) != 0) {
1154 	l1 = gl_strlen(fixup_last_prompt);
1155 	l2 = gl_strlen(prompt);
1156 	gl_cnt = gl_cnt + l1 - l2;
1157 	strncpy(fixup_last_prompt, prompt, sizeof(fixup_last_prompt));
1158 	gl_putc('\r');
1159 	gl_puts(prompt);
1160 	gl_pos = fixup_gl_shift;
1161         gl_width = gl_termw - l2;
1162 	change = 0;
1163     }
1164     pad = (fixup_off_right)? gl_width - 1 : gl_cnt - fixup_gl_shift;   /* old length */
1165     backup = gl_pos - fixup_gl_shift;
1166     if (change >= 0) {
1167         gl_cnt = strlen(cligen_buf(h));
1168         if (change > gl_cnt)
1169 	    change = gl_cnt;
1170     }
1171     if (cursor > gl_cnt) {
1172 	if (cursor != cligen_buf_size(h))		/* cligen_buf_size(h) means end of line */
1173 	    gl_putc('\007');
1174 	cursor = gl_cnt;
1175     }
1176     if (cursor < 0) {
1177 	gl_putc('\007');
1178 	cursor = 0;
1179     }
1180     if (change >= 0) {		/* text changed */
1181 	if (change < fixup_gl_shift + fixup_off_left) {
1182 	    left = fixup_gl_shift;
1183 	} else {
1184 	    left = change;
1185 	    backup = gl_pos - change;
1186 	}
1187 	right = gl_cnt;
1188 	new_right = (gl_extent && (right > left + gl_extent))?
1189 	    left + gl_extent : right;
1190     }
1191     pad -= gl_cnt - fixup_gl_shift;
1192     pad = (pad < 0)? 0 : pad;
1193     if (left <= right) {		/* clean up screen */
1194 	for (p=left+backup-1; p >= left; p--){
1195 	    if (wrap(p, plen))
1196 		unwrap_line();
1197 	    else
1198 		gl_putc('\b');
1199 	}
1200 	if (left == fixup_gl_shift && fixup_off_left) {
1201 	    gl_putc('$');
1202 	    left++;
1203         }
1204 	for (p=left; p < new_right; p++){
1205 	    gl_putc(cligen_buf(h)[p]);
1206 	    if (wrap(p, plen))
1207 		wrap_line();
1208 	}
1209 	gl_pos = new_right;
1210 	for (p=new_right; p < new_right+pad; p++){ /* erase remains of prev line */
1211 	    gl_putc(' ');
1212 	    if (wrap(p, plen))
1213 		wrap_line();
1214 	}
1215 	gl_pos += pad;
1216     }
1217     /* move to final cursor location */
1218     if (gl_pos - cursor > 0) {
1219 	for (p=gl_pos; p > cursor; p--){
1220 	    if (wrap(p-1, plen))
1221 		unwrap_line();
1222 	    else
1223 		gl_putc('\b');
1224 	}
1225     }
1226     else {
1227 	for (i=gl_pos; i < cursor; i++)
1228 	    gl_putc(cligen_buf(h)[i]);
1229     }
1230     gl_pos = cursor;
1231 }
1232 
1233 
1234 /*! Redrawing or moving within line
1235  *
1236  * This function is used both for redrawing when input changes or for
1237  * moving within the input line.  The parameters are:
1238  *   prompt:  compared to last_prompt[] for changes;
1239  *   change : the index of the start of changes in the input buffer,
1240  *            with -1 indicating no changes, -2 indicating we're on
1241  *            a new line, redraw everything.
1242  *   cursor : the desired location of the cursor after the call.
1243  *            A value of cligen_buf_size(h) can be used  to indicate the cursor should
1244  *            move just past the end of the input line.
1245  */
1246 static void
gl_fixup_scroll(cligen_handle h,char * prompt,int change,int cursor)1247 gl_fixup_scroll(cligen_handle h,
1248 		char         *prompt,
1249 		int           change,
1250 		int           cursor)
1251 {
1252     int          left = 0, right = -1;		/* bounds for redraw */
1253     int          pad;		/* how much to erase at end of line */
1254     int          backup;        /* how far to backup before fixing */
1255     int          new_shift;     /* value of shift based on cursor */
1256     int          extra;         /* adjusts when shift (scroll) happens */
1257     int          i;
1258     int          new_right = -1; /* alternate right bound, using gl_extent */
1259     int          l1, l2;
1260 
1261     if (change == -2) {   /* reset */
1262 	gl_pos = gl_cnt = fixup_gl_shift = fixup_off_right = fixup_off_left = 0;
1263 	gl_putc('\r');
1264 	gl_puts(prompt);
1265 	strncpy(fixup_last_prompt, prompt, sizeof(fixup_last_prompt));
1266 	change = 0;
1267         gl_width = gl_termw - gl_strlen(prompt);
1268     } else if (strcmp(prompt, fixup_last_prompt) != 0) {
1269 	l1 = gl_strlen(fixup_last_prompt);
1270 	l2 = gl_strlen(prompt);
1271 	gl_cnt = gl_cnt + l1 - l2;
1272 	strncpy(fixup_last_prompt, prompt, sizeof(fixup_last_prompt));
1273 	gl_putc('\r');
1274 	gl_puts(prompt);
1275 	gl_pos = fixup_gl_shift;
1276         gl_width = gl_termw - l2;
1277 	change = 0;
1278     }
1279     pad = (fixup_off_right)? gl_width - 1 : gl_cnt - fixup_gl_shift;   /* old length */
1280     backup = gl_pos - fixup_gl_shift;
1281     if (change >= 0) {
1282         gl_cnt = strlen(cligen_buf(h));
1283         if (change > gl_cnt)
1284 	    change = gl_cnt;
1285     }
1286     if (cursor > gl_cnt) {
1287 	if (cursor != cligen_buf_size(h))		/* cligen_buf_size(h) means end of line */
1288 	    gl_putc('\007');
1289 	cursor = gl_cnt;
1290     }
1291     if (cursor < 0) {
1292 	gl_putc('\007');
1293 	cursor = 0;
1294     }
1295     if (fixup_off_right || (fixup_off_left && cursor < fixup_gl_shift + gl_width - gl_scrollw / 2)){
1296 	extra = 2;			/* shift the scrolling boundary */
1297     }
1298     else
1299 	extra = 0;
1300 
1301     new_shift = cursor + extra + gl_scrollw - gl_width;
1302     if (new_shift > 0) {
1303 	new_shift /= gl_scrollw;
1304 	new_shift *= gl_scrollw;
1305     } else
1306 	new_shift = 0;
1307     if (new_shift != fixup_gl_shift) {	/* scroll occurs */
1308 	fixup_gl_shift = new_shift;
1309 	fixup_off_left = (fixup_gl_shift)? 1 : 0;
1310 	fixup_off_right = (gl_cnt > fixup_gl_shift + gl_width - 1)? 1 : 0;
1311 	left = fixup_gl_shift;
1312 	new_right = right = (fixup_off_right)? fixup_gl_shift + gl_width - 2 : gl_cnt;
1313     } else if (change >= 0) {		/* no scroll, but text changed */
1314 	if (change < fixup_gl_shift + fixup_off_left) {
1315 	    left = fixup_gl_shift;
1316 	} else {
1317 	    left = change;
1318 	    backup = gl_pos - change;
1319 	}
1320 	fixup_off_right = (gl_cnt > fixup_gl_shift + gl_width - 1)? 1 : 0;
1321 	right = (fixup_off_right)? fixup_gl_shift + gl_width - 2 : gl_cnt;
1322 	new_right = (gl_extent && (right > left + gl_extent))?
1323 	    left + gl_extent : right;
1324     }
1325     pad -= (fixup_off_right)? gl_width - 1 : gl_cnt - fixup_gl_shift;
1326     pad = (pad < 0)? 0 : pad;
1327     if (left <= right) {		/* clean up screen */
1328 	for (i=0; i < backup; i++)
1329 	    gl_putc('\b');
1330 	if (left == fixup_gl_shift && fixup_off_left) {
1331 	    gl_putc('$');
1332 	    left++;
1333         }
1334 	for (i=left; i < new_right; i++)
1335 	    gl_putc(cligen_buf(h)[i]);
1336 	gl_pos = new_right;
1337 	if (fixup_off_right && new_right == right) {
1338 	    gl_putc('$');
1339 	    gl_pos++;
1340 	} else {
1341 	    for (i=0; i < pad; i++)	/* erase remains of prev line */
1342 		gl_putc(' ');
1343 	    gl_pos += pad;
1344 	}
1345     }
1346     i = gl_pos - cursor;		/* move to final cursor location */
1347     if (i > 0) {
1348 	while (i--)
1349 	    gl_putc('\b');
1350     } else {
1351 	for (i=gl_pos; i < cursor; i++)
1352 	    gl_putc(cligen_buf(h)[i]);
1353     }
1354     gl_pos = cursor;
1355 }
1356 
1357 static inline void
gl_fixup(cligen_handle h,char * prompt,int change,int cursor)1358 gl_fixup(cligen_handle h,
1359 	 char         *prompt,
1360 	 int           change,
1361 	 int           cursor)
1362 {
1363     if (gl_scrolling_mode)
1364 	return gl_fixup_scroll(h, prompt, change, cursor);
1365     else
1366 	return gl_fixup_noscroll(h, prompt, change, cursor);
1367 }
1368 
1369 /******************* strlen stuff **************************************/
1370 
1371 void
gl_strwidth(size_t (* func)())1372 gl_strwidth(size_t (*func)())
1373 {
1374     if (func != 0) {
1375 	gl_strlen = func;
1376     }
1377 }
1378 
1379 
1380 /******************* Search stuff **************************************/
1381 
1382 
1383 static void
search_update(cligen_handle h,int c)1384 search_update(cligen_handle h,
1385 	      int           c)
1386 {
1387     if (c == 0) {
1388 	search_pos = 0;
1389         search_string[0] = 0;
1390         search_prompt[0] = '?';
1391         search_prompt[1] = ' ';
1392         search_prompt[2] = 0;
1393     } else if (c > 0){
1394 	if (search_pos+1 < SEARCH_LEN) {
1395 	    search_string[search_pos] = c;
1396 	    search_string[search_pos+1] = 0;
1397 	    search_prompt[search_pos] = c;
1398 	    search_prompt[search_pos+1] = '?';
1399 	    search_prompt[search_pos+2] = ' ';
1400 	    search_prompt[search_pos+3] = 0;
1401 	    search_pos++;
1402 	}
1403     } else {
1404 	if (search_pos > 0) {
1405 	    search_pos--;
1406             search_string[search_pos] = 0;
1407             search_prompt[search_pos] = '?';
1408             search_prompt[search_pos+1] = ' ';
1409             search_prompt[search_pos+2] = 0;
1410 	} else {
1411 	    gl_putc('\007');
1412 	    hist_pos_set(h, hist_last_get(h));
1413 	}
1414     }
1415 }
1416 
1417 /*! Search addchar
1418  * @param[in]  h     CLIgen handle
1419  */
1420 static void
search_addchar(cligen_handle h,int c)1421 search_addchar(cligen_handle h,
1422 	       int           c)
1423 {
1424     char *loc;
1425 
1426     search_update(h, c);
1427     if (c < 0) {
1428 	if (search_pos > 0) {
1429 	    hist_pos_set(h, search_last);
1430 	} else {
1431 	    cligen_buf(h)[0] = 0;
1432 	    hist_pos_set(h, hist_last_get(h));
1433 	}
1434 	hist_copy_pos(h);
1435     }
1436     if ((loc = strstr(cligen_buf(h), search_string)) != 0) {
1437 	gl_fixup(h, search_prompt, 0, loc - cligen_buf(h));
1438     } else if (search_pos > 0) {
1439         if (search_forw_flg) {
1440 	    search_forw(h, 0);
1441         } else {
1442 	    search_back(h, 0);
1443         }
1444     } else {
1445 	gl_fixup(h, search_prompt, 0, 0);
1446     }
1447 }
1448 
1449 /*! Search terminate
1450  * @param[in]  h     CLIgen handle
1451  */
1452 static void
search_term(cligen_handle h)1453 search_term(cligen_handle h)
1454 {
1455     gl_search_mode = 0;
1456     if (cligen_buf(h)[0] == 0)		/* not found, reset hist list */
1457         hist_pos_set(h, hist_last_get(h));
1458     if (gl_in_hook)
1459 	gl_in_hook(h, cligen_buf(h));
1460     gl_fixup(h, cligen_prompt(h), 0, gl_pos);
1461 }
1462 
1463 /*! Search backwards
1464  * @param[in]  h     CLIgen handle
1465  */
1466 static void
search_back(cligen_handle h,int new_search)1467 search_back(cligen_handle h,
1468 	    int           new_search)
1469 {
1470     int    found = 0;
1471     char  *p, *loc;
1472     int    last;
1473 
1474     search_forw_flg = 0;
1475     if (gl_search_mode == 0) {
1476 	last = hist_last_get(h);
1477 	hist_pos_set(h, last);
1478 	search_last = last;
1479 	search_update(h, 0);
1480 	gl_search_mode = 1;
1481         cligen_buf(h)[0] = 0;
1482 	gl_fixup(h, search_prompt, 0, 0);
1483     } else if (search_pos > 0) {
1484 	while (!found) {
1485 	    p = hist_prev(h);
1486 	    if (*p == 0) {		/* not found, done looking */
1487 	       cligen_buf(h)[0] = 0;
1488 	       gl_fixup(h, search_prompt, 0, 0);
1489 	       found = 1;
1490 	    } else if ((loc = strstr(p, search_string)) != 0) {
1491 		strncpy(cligen_buf(h), p, cligen_buf_size(h));
1492 		gl_fixup(h, search_prompt, 0, loc - p);
1493 	       if (new_search)
1494 		   search_last = hist_pos(h);
1495 	       found = 1;
1496 	    }
1497 	}
1498     } else {
1499         gl_putc('\007');
1500     }
1501 }
1502 
1503 /*! Search forward
1504  * @param[in]  h     CLIgen handle
1505  */
1506 static void
search_forw(cligen_handle h,int new_search)1507 search_forw(cligen_handle h,
1508 	    int           new_search)
1509 {
1510     int    found = 0;
1511     char  *p, *loc;
1512     int    last;
1513 
1514     search_forw_flg = 1;
1515     if (gl_search_mode == 0) {
1516 	last = hist_last_get(h);
1517 	hist_pos_set(h, last);
1518 	search_last = last;
1519 
1520 	search_update(h, 0);
1521 	gl_search_mode = 1;
1522         cligen_buf(h)[0] = 0;
1523 	gl_fixup(h, search_prompt, 0, 0);
1524     } else if (search_pos > 0) {
1525 	while (!found) {
1526 	    p = hist_next(h);
1527 	    if (*p == 0) {		/* not found, done looking */
1528 	       cligen_buf(h)[0] = 0;
1529 	       gl_fixup(h, search_prompt, 0, 0);
1530 	       found = 1;
1531 	    } else if ((loc = strstr(p, search_string)) != 0) {
1532 		strncpy(cligen_buf(h), p, cligen_buf_size(h));
1533 		gl_fixup(h, search_prompt, 0, loc - p);
1534 	       if (new_search)
1535 		   search_last = hist_pos(h);
1536 	       found = 1;
1537 	    }
1538 	}
1539     } else {
1540         gl_putc('\007');
1541     }
1542 }
1543 
1544