Home | History | Annotate | Line # | Download | only in libedit
read.c revision 1.19
      1 /*	$NetBSD: read.c,v 1.19 2001/01/10 07:45:41 jdolecek Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1992, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * This code is derived from software contributed to Berkeley by
      8  * Christos Zoulas of Cornell University.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *	This product includes software developed by the University of
     21  *	California, Berkeley and its contributors.
     22  * 4. Neither the name of the University nor the names of its contributors
     23  *    may be used to endorse or promote products derived from this software
     24  *    without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     36  * SUCH DAMAGE.
     37  */
     38 
     39 #include <sys/cdefs.h>
     40 #if !defined(lint) && !defined(SCCSID)
     41 #if 0
     42 static char sccsid[] = "@(#)read.c	8.1 (Berkeley) 6/4/93";
     43 #else
     44 __RCSID("$NetBSD: read.c,v 1.19 2001/01/10 07:45:41 jdolecek Exp $");
     45 #endif
     46 #endif /* not lint && not SCCSID */
     47 
     48 /*
     49  * read.c: Clean this junk up! This is horrible code.
     50  *	   Terminal read functions
     51  */
     52 #include "sys.h"
     53 #include <errno.h>
     54 #include <unistd.h>
     55 #include <stdlib.h>
     56 #include "el.h"
     57 
     58 #define	OKCMD	-1
     59 
     60 private int	read__fixio(int, int);
     61 private int	read_preread(EditLine *);
     62 private int	read_getcmd(EditLine *, el_action_t *, char *);
     63 private int	read_char(EditLine *, char *);
     64 
     65 #ifdef DEBUG_EDIT
     66 private void
     67 read_debug(EditLine *el)
     68 {
     69 
     70 	if (el->el_line.cursor > el->el_line.lastchar)
     71 		(void) fprintf(el->el_errfile, "cursor > lastchar\r\n");
     72 	if (el->el_line.cursor < el->el_line.buffer)
     73 		(void) fprintf(el->el_errfile, "cursor < buffer\r\n");
     74 	if (el->el_line.cursor > el->el_line.limit)
     75 		(void) fprintf(el->el_errfile, "cursor > limit\r\n");
     76 	if (el->el_line.lastchar > el->el_line.limit)
     77 		(void) fprintf(el->el_errfile, "lastchar > limit\r\n");
     78 	if (el->el_line.limit != &el->el_line.buffer[EL_BUFSIZ - 2])
     79 		(void) fprintf(el->el_errfile, "limit != &buffer[EL_BUFSIZ-2]\r\n");
     80 }
     81 #endif /* DEBUG_EDIT */
     82 
     83 
     84 /* read__fixio():
     85  *	Try to recover from a read error
     86  */
     87 /* ARGSUSED */
     88 private int
     89 read__fixio(int fd, int e)
     90 {
     91 
     92 	switch (e) {
     93 	case -1:		/* Make sure that the code is reachable */
     94 
     95 #ifdef EWOULDBLOCK
     96 	case EWOULDBLOCK:
     97 #ifndef TRY_AGAIN
     98 #define	TRY_AGAIN
     99 #endif
    100 #endif /* EWOULDBLOCK */
    101 
    102 #if defined(POSIX) && defined(EAGAIN)
    103 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
    104 	case EAGAIN:
    105 #ifndef TRY_AGAIN
    106 #define	TRY_AGAIN
    107 #endif
    108 #endif /* EWOULDBLOCK && EWOULDBLOCK != EAGAIN */
    109 #endif /* POSIX && EAGAIN */
    110 
    111 		e = 0;
    112 #ifdef TRY_AGAIN
    113 #if defined(F_SETFL) && defined(O_NDELAY)
    114 		if ((e = fcntl(fd, F_GETFL, 0)) == -1)
    115 			return (-1);
    116 
    117 		if (fcntl(fd, F_SETFL, e & ~O_NDELAY) == -1)
    118 			return (-1);
    119 		else
    120 			e = 1;
    121 #endif /* F_SETFL && O_NDELAY */
    122 
    123 #ifdef FIONBIO
    124 		{
    125 			int zero = 0;
    126 
    127 			if (ioctl(fd, FIONBIO, (ioctl_t) & zero) == -1)
    128 				return (-1);
    129 			else
    130 				e = 1;
    131 		}
    132 #endif /* FIONBIO */
    133 
    134 #endif /* TRY_AGAIN */
    135 		return (e ? 0 : -1);
    136 
    137 	case EINTR:
    138 		return (0);
    139 
    140 	default:
    141 		return (-1);
    142 	}
    143 }
    144 
    145 
    146 /* read_preread():
    147  *	Try to read the stuff in the input queue;
    148  */
    149 private int
    150 read_preread(EditLine *el)
    151 {
    152 	int chrs = 0;
    153 
    154 	if (el->el_chared.c_macro.nline) {
    155 		el_free((ptr_t) el->el_chared.c_macro.nline);
    156 		el->el_chared.c_macro.nline = NULL;
    157 	}
    158 	if (el->el_tty.t_mode == ED_IO)
    159 		return (0);
    160 
    161 #ifdef FIONREAD
    162 	(void) ioctl(el->el_infd, FIONREAD, (ioctl_t) & chrs);
    163 	if (chrs > 0) {
    164 		char buf[EL_BUFSIZ];
    165 
    166 		chrs = read(el->el_infd, buf,
    167 		    (size_t) MIN(chrs, EL_BUFSIZ - 1));
    168 		if (chrs > 0) {
    169 			buf[chrs] = '\0';
    170 			el->el_chared.c_macro.nline = strdup(buf);
    171 			el_push(el, el->el_chared.c_macro.nline);
    172 		}
    173 	}
    174 #endif /* FIONREAD */
    175 
    176 	return (chrs > 0);
    177 }
    178 
    179 
    180 /* el_push():
    181  *	Push a macro
    182  */
    183 public void
    184 el_push(EditLine *el, const char *str)
    185 {
    186 	c_macro_t *ma = &el->el_chared.c_macro;
    187 
    188 	if (str != NULL && ma->level + 1 < EL_MAXMACRO) {
    189 		ma->level++;
    190 		/* LINTED const cast */
    191 		ma->macro[ma->level] = (char *) str;
    192 	} else {
    193 		term_beep(el);
    194 		term__flush();
    195 	}
    196 }
    197 
    198 
    199 /* read_getcmd():
    200  *	Return next command from the input stream.
    201  */
    202 private int
    203 read_getcmd(EditLine *el, el_action_t *cmdnum, char *ch)
    204 {
    205 	el_action_t cmd = ED_UNASSIGNED;
    206 	int num;
    207 
    208 	while (cmd == ED_UNASSIGNED || cmd == ED_SEQUENCE_LEAD_IN) {
    209 		if ((num = el_getc(el, ch)) != 1)	/* if EOF or error */
    210 			return (num);
    211 
    212 #ifdef	KANJI
    213 		if ((*ch & 0200)) {
    214 			el->el_state.metanext = 0;
    215 			cmd = CcViMap[' '];
    216 			break;
    217 		} else
    218 #endif /* KANJI */
    219 
    220 		if (el->el_state.metanext) {
    221 			el->el_state.metanext = 0;
    222 			*ch |= 0200;
    223 		}
    224 		cmd = el->el_map.current[(unsigned char) *ch];
    225 		if (cmd == ED_SEQUENCE_LEAD_IN) {
    226 			key_value_t val;
    227 			switch (key_get(el, ch, &val)) {
    228 			case XK_CMD:
    229 				cmd = val.cmd;
    230 				break;
    231 			case XK_STR:
    232 				el_push(el, val.str);
    233 				break;
    234 #ifdef notyet
    235 			case XK_EXE:
    236 				/* XXX: In the future to run a user function */
    237 				RunCommand(val.str);
    238 				break;
    239 #endif
    240 			default:
    241 				EL_ABORT((el->el_errfile, "Bad XK_ type \n"));
    242 				break;
    243 			}
    244 		}
    245 		if (el->el_map.alt == NULL)
    246 			el->el_map.current = el->el_map.key;
    247 	}
    248 	*cmdnum = cmd;
    249 	return (OKCMD);
    250 }
    251 
    252 
    253 /* read_char():
    254  *	Read a character from the tty.
    255  */
    256 private int
    257 read_char(EditLine *el, char *cp)
    258 {
    259 	int num_read;
    260 	int tried = 0;
    261 
    262 	while ((num_read = read(el->el_infd, cp, 1)) == -1)
    263 		if (!tried && read__fixio(el->el_infd, errno) == 0)
    264 			tried = 1;
    265 		else {
    266 			*cp = '\0';
    267 			return (-1);
    268 		}
    269 
    270 	return (num_read);
    271 }
    272 
    273 
    274 /* el_getc():
    275  *	Read a character
    276  */
    277 public int
    278 el_getc(EditLine *el, char *cp)
    279 {
    280 	int num_read;
    281 	c_macro_t *ma = &el->el_chared.c_macro;
    282 
    283 	term__flush();
    284 	for (;;) {
    285 		if (ma->level < 0) {
    286 			if (!read_preread(el))
    287 				break;
    288 		}
    289 		if (ma->level < 0)
    290 			break;
    291 
    292 		if (*ma->macro[ma->level] == 0) {
    293 			ma->level--;
    294 			continue;
    295 		}
    296 		*cp = *ma->macro[ma->level]++ & 0377;
    297 		if (*ma->macro[ma->level] == 0) {	/* Needed for QuoteMode
    298 							 * On */
    299 			ma->level--;
    300 		}
    301 		return (1);
    302 	}
    303 
    304 #ifdef DEBUG_READ
    305 	(void) fprintf(el->el_errfile, "Turning raw mode on\n");
    306 #endif /* DEBUG_READ */
    307 	if (tty_rawmode(el) < 0)/* make sure the tty is set up correctly */
    308 		return (0);
    309 
    310 #ifdef DEBUG_READ
    311 	(void) fprintf(el->el_errfile, "Reading a character\n");
    312 #endif /* DEBUG_READ */
    313 	num_read = read_char(el, cp);
    314 #ifdef DEBUG_READ
    315 	(void) fprintf(el->el_errfile, "Got it %c\n", *cp);
    316 #endif /* DEBUG_READ */
    317 	return (num_read);
    318 }
    319 
    320 
    321 public const char *
    322 el_gets(EditLine *el, int *nread)
    323 {
    324 	int retval;
    325 	el_action_t cmdnum = 0;
    326 	int num;		/* how many chars we have read at NL */
    327 	char ch;
    328 #ifdef FIONREAD
    329 	c_macro_t *ma = &el->el_chared.c_macro;
    330 #endif /* FIONREAD */
    331 
    332 	if (el->el_flags & HANDLE_SIGNALS)
    333 		sig_set(el);
    334 
    335 	if (el->el_flags & NO_TTY) {
    336 		char *cp = el->el_line.buffer;
    337 		size_t idx;
    338 
    339 		while (read_char(el, cp) == 1) {
    340 			/* make sure there is space for next character */
    341 			if (cp + 1 >= el->el_line.limit) {
    342 				idx = (cp - el->el_line.buffer);
    343 				if (!ch_enlargebufs(el, 2))
    344 					break;
    345 				cp = &el->el_line.buffer[idx];
    346 			}
    347 			cp++;
    348 			if (cp[-1] == '\r' || cp[-1] == '\n')
    349 				break;
    350 		}
    351 
    352 		el->el_line.cursor = el->el_line.lastchar = cp;
    353 		*cp = '\0';
    354 		if (nread)
    355 			*nread = el->el_line.cursor - el->el_line.buffer;
    356 		return (el->el_line.buffer);
    357 	}
    358 	re_clear_display(el);	/* reset the display stuff */
    359 	ch_reset(el);
    360 
    361 #ifdef FIONREAD
    362 	if (el->el_tty.t_mode == EX_IO && ma->level < 0) {
    363 		long chrs = 0;
    364 
    365 		(void) ioctl(el->el_infd, FIONREAD, (ioctl_t) & chrs);
    366 		if (chrs == 0) {
    367 			if (tty_rawmode(el) < 0) {
    368 				if (nread)
    369 					*nread = 0;
    370 				return (NULL);
    371 			}
    372 		}
    373 	}
    374 #endif /* FIONREAD */
    375 
    376 	re_refresh(el);		/* print the prompt */
    377 
    378 	if (el->el_flags & EDIT_DISABLED) {
    379 		char *cp = el->el_line.buffer;
    380 		size_t idx;
    381 
    382 		term__flush();
    383 
    384 		while (read_char(el, cp) == 1) {
    385 			/* make sure there is space next character */
    386 			if (cp + 1 >= el->el_line.limit) {
    387 				idx = (cp - el->el_line.buffer);
    388 				if (!ch_enlargebufs(el, 2))
    389 					break;
    390 				cp = &el->el_line.buffer[idx];
    391 			}
    392 			cp++;
    393 			if (cp[-1] == '\r' || cp[-1] == '\n')
    394 				break;
    395 		}
    396 
    397 		el->el_line.cursor = el->el_line.lastchar = cp;
    398 		*cp = '\0';
    399 		if (nread)
    400 			*nread = el->el_line.cursor - el->el_line.buffer;
    401 		return (el->el_line.buffer);
    402 	}
    403 	for (num = OKCMD; num == OKCMD;) {	/* while still editing this
    404 						 * line */
    405 #ifdef DEBUG_EDIT
    406 		read_debug(el);
    407 #endif /* DEBUG_EDIT */
    408 		/* if EOF or error */
    409 		if ((num = read_getcmd(el, &cmdnum, &ch)) != OKCMD) {
    410 #ifdef DEBUG_READ
    411 			(void) fprintf(el->el_errfile,
    412 			    "Returning from el_gets %d\n", num);
    413 #endif /* DEBUG_READ */
    414 			break;
    415 		}
    416 		if ((int) cmdnum >= el->el_map.nfunc) {	/* BUG CHECK command */
    417 #ifdef DEBUG_EDIT
    418 			(void) fprintf(el->el_errfile,
    419 			    "ERROR: illegal command from key 0%o\r\n", ch);
    420 #endif /* DEBUG_EDIT */
    421 			continue;	/* try again */
    422 		}
    423 		/* now do the real command */
    424 #ifdef DEBUG_READ
    425 		{
    426 			el_bindings_t *b;
    427 			for (b = el->el_map.help; b->name; b++)
    428 				if (b->func == cmdnum)
    429 					break;
    430 			if (b->name)
    431 				(void) fprintf(el->el_errfile,
    432 				    "Executing %s\n", b->name);
    433 			else
    434 				(void) fprintf(el->el_errfile,
    435 				    "Error command = %d\n", cmdnum);
    436 		}
    437 #endif /* DEBUG_READ */
    438 		retval = (*el->el_map.func[cmdnum]) (el, ch);
    439 
    440 		/* save the last command here */
    441 		el->el_state.lastcmd = cmdnum;
    442 
    443 		/* use any return value */
    444 		switch (retval) {
    445 		case CC_CURSOR:
    446 			el->el_state.argument = 1;
    447 			el->el_state.doingarg = 0;
    448 			re_refresh_cursor(el);
    449 			break;
    450 
    451 		case CC_REDISPLAY:
    452 			re_clear_lines(el);
    453 			re_clear_display(el);
    454 			/* FALLTHROUGH */
    455 
    456 		case CC_REFRESH:
    457 			el->el_state.argument = 1;
    458 			el->el_state.doingarg = 0;
    459 			re_refresh(el);
    460 			break;
    461 
    462 		case CC_REFRESH_BEEP:
    463 			el->el_state.argument = 1;
    464 			el->el_state.doingarg = 0;
    465 			re_refresh(el);
    466 			term_beep(el);
    467 			break;
    468 
    469 		case CC_NORM:	/* normal char */
    470 			el->el_state.argument = 1;
    471 			el->el_state.doingarg = 0;
    472 			break;
    473 
    474 		case CC_ARGHACK:	/* Suggested by Rich Salz */
    475 			/* <rsalz (at) pineapple.bbn.com> */
    476 			break;	/* keep going... */
    477 
    478 		case CC_EOF:	/* end of file typed */
    479 			num = 0;
    480 			break;
    481 
    482 		case CC_NEWLINE:	/* normal end of line */
    483 			num = el->el_line.lastchar - el->el_line.buffer;
    484 			break;
    485 
    486 		case CC_FATAL:	/* fatal error, reset to known state */
    487 #ifdef DEBUG_READ
    488 			(void) fprintf(el->el_errfile,
    489 			    "*** editor fatal ERROR ***\r\n\n");
    490 #endif /* DEBUG_READ */
    491 			/* put (real) cursor in a known place */
    492 			re_clear_display(el);	/* reset the display stuff */
    493 			ch_reset(el);	/* reset the input pointers */
    494 			re_refresh(el);	/* print the prompt again */
    495 			el->el_state.argument = 1;
    496 			el->el_state.doingarg = 0;
    497 			break;
    498 
    499 		case CC_ERROR:
    500 		default:	/* functions we don't know about */
    501 #ifdef DEBUG_READ
    502 			(void) fprintf(el->el_errfile,
    503 			    "*** editor ERROR ***\r\n\n");
    504 #endif /* DEBUG_READ */
    505 			el->el_state.argument = 1;
    506 			el->el_state.doingarg = 0;
    507 			term_beep(el);
    508 			term__flush();
    509 			break;
    510 		}
    511 	}
    512 
    513 				/* make sure the tty is set up correctly */
    514 	(void) tty_cookedmode(el);
    515 	term__flush();		/* flush any buffered output */
    516 	if (el->el_flags & HANDLE_SIGNALS)
    517 		sig_clr(el);
    518 	if (nread)
    519 		*nread = num;
    520 	return (num ? el->el_line.buffer : NULL);
    521 }
    522