1/*	$OpenBSD: read.c,v 1.49 2021/08/13 10:21:25 schwarze Exp $	*/
2/*	$NetBSD: read.c,v 1.100 2016/05/24 19:31:27 christos Exp $	*/
3
4/*-
5 * Copyright (c) 1992, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Christos Zoulas of Cornell University.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "config.h"
37
38/*
39 * read.c: Clean this junk up! This is horrible code.
40 *	   Terminal read functions
41 */
42#include <sys/ioctl.h>
43
44#include <ctype.h>
45#include <errno.h>
46#include <limits.h>
47#include <stdlib.h>
48#include <string.h>
49#include <unistd.h>
50
51#include "el.h"
52#include "fcns.h"
53#include "read.h"
54
55#define	EL_MAXMACRO	10
56
57struct macros {
58	wchar_t	**macro;
59	int	  level;
60	int	  offset;
61};
62
63struct el_read_t {
64	struct macros	 macros;
65	el_rfunc_t	 read_char;	/* Function to read a character. */
66	int		 read_errno;
67};
68
69static int	read_char(EditLine *, wchar_t *);
70static int	read_getcmd(EditLine *, el_action_t *, wchar_t *);
71static void	read_clearmacros(struct macros *);
72static void	read_pop(struct macros *);
73
74/* read_init():
75 *	Initialize the read stuff
76 */
77protected int
78read_init(EditLine *el)
79{
80	struct macros *ma;
81
82	if ((el->el_read = malloc(sizeof(*el->el_read))) == NULL)
83		return -1;
84
85	ma = &el->el_read->macros;
86	if ((ma->macro = reallocarray(NULL, EL_MAXMACRO,
87	    sizeof(*ma->macro))) == NULL) {
88		free(el->el_read);
89		return -1;
90	}
91	ma->level = -1;
92	ma->offset = 0;
93
94	/* builtin read_char */
95	el->el_read->read_char = read_char;
96	return 0;
97}
98
99/* el_read_end():
100 *	Free the data structures used by the read stuff.
101 */
102protected void
103read_end(struct el_read_t *el_read)
104{
105	read_clearmacros(&el_read->macros);
106	free(el_read->macros.macro);
107	el_read->macros.macro = NULL;
108}
109
110/* el_read_setfn():
111 *	Set the read char function to the one provided.
112 *	If it is set to EL_BUILTIN_GETCFN, then reset to the builtin one.
113 */
114protected int
115el_read_setfn(struct el_read_t *el_read, el_rfunc_t rc)
116{
117	el_read->read_char = (rc == EL_BUILTIN_GETCFN) ? read_char : rc;
118	return 0;
119}
120
121
122/* el_read_getfn():
123 *	return the current read char function, or EL_BUILTIN_GETCFN
124 *	if it is the default one
125 */
126protected el_rfunc_t
127el_read_getfn(struct el_read_t *el_read)
128{
129	return el_read->read_char == read_char ?
130	    EL_BUILTIN_GETCFN : el_read->read_char;
131}
132
133
134/* el_push():
135 *	Push a macro
136 */
137void
138el_wpush(EditLine *el, const wchar_t *str)
139{
140	struct macros *ma = &el->el_read->macros;
141
142	if (str != NULL && ma->level + 1 < EL_MAXMACRO) {
143		ma->level++;
144		if ((ma->macro[ma->level] = wcsdup(str)) != NULL)
145			return;
146		ma->level--;
147	}
148	terminal_beep(el);
149	terminal__flush(el);
150}
151
152
153/* read_getcmd():
154 *	Get next command from the input stream,
155 *	return 0 on success or -1 on EOF or error.
156 *	Character values > 255 are not looked up in the map, but inserted.
157 */
158static int
159read_getcmd(EditLine *el, el_action_t *cmdnum, wchar_t *ch)
160{
161	static const wchar_t meta = (wchar_t)0x80;
162	el_action_t cmd;
163	int num;
164
165	do {
166		if ((num = el_wgetc(el, ch)) != 1)
167			return -1;
168
169#ifdef	KANJI
170		if ((*ch & meta)) {
171			el->el_state.metanext = 0;
172			cmd = CcViMap[' '];
173			break;
174		} else
175#endif /* KANJI */
176
177		if (el->el_state.metanext) {
178			el->el_state.metanext = 0;
179			*ch |= meta;
180		}
181		if (*ch >= N_KEYS)
182			cmd = ED_INSERT;
183		else
184			cmd = el->el_map.current[(unsigned char) *ch];
185		if (cmd == ED_SEQUENCE_LEAD_IN) {
186			keymacro_value_t val;
187			switch (keymacro_get(el, ch, &val)) {
188			case XK_CMD:
189				cmd = val.cmd;
190				break;
191			case XK_STR:
192				el_wpush(el, val.str);
193				break;
194			case XK_NOD:
195				return -1;
196			default:
197				EL_ABORT((el->el_errfile, "Bad XK_ type \n"));
198				break;
199			}
200		}
201	} while (cmd == ED_SEQUENCE_LEAD_IN);
202	*cmdnum = cmd;
203	return 0;
204}
205
206/* read_char():
207 *	Read a character from the tty.
208 */
209static int
210read_char(EditLine *el, wchar_t *cp)
211{
212	char cbuf[MB_LEN_MAX];
213	int cbp = 0;
214
215 again:
216	el->el_signal->sig_no = 0;
217	switch (read(el->el_infd, cbuf + cbp, 1)) {
218	case -1:
219		if (errno == EINTR) {
220			switch (el->el_signal->sig_no) {
221			case SIGCONT:
222				el_set(el, EL_REFRESH);
223				/*FALLTHROUGH*/
224			case SIGWINCH:
225				sig_set(el);
226				goto again;
227			default:
228				break;
229			}
230		}
231		*cp = L'\0';
232		return -1;
233	case 0:
234		*cp = L'\0';
235		return 0;
236	default:
237		break;
238	}
239
240	for (;;) {
241		mbstate_t mbs;
242
243		++cbp;
244		/* This only works because UTF8 is stateless. */
245		memset(&mbs, 0, sizeof(mbs));
246		switch (mbrtowc(cp, cbuf, cbp, &mbs)) {
247		case (size_t)-1:
248			if (cbp > 1) {
249				/*
250				 * Invalid sequence, discard all bytes
251				 * except the last one.
252				 */
253				cbuf[0] = cbuf[cbp - 1];
254				cbp = 0;
255				break;
256			} else {
257				/* Invalid byte, discard it. */
258				cbp = 0;
259				goto again;
260			}
261		case (size_t)-2:
262			/*
263			 * We don't support other multibyte charsets.
264			 * The second condition shouldn't happen
265			 * and is here merely for additional safety.
266			 */
267			if ((el->el_flags & CHARSET_IS_UTF8) == 0 ||
268			    cbp >= MB_LEN_MAX) {
269				errno = EILSEQ;
270				*cp = L'\0';
271				return -1;
272			}
273			/* Incomplete sequence, read another byte. */
274			goto again;
275		default:
276			/* Valid character, process it. */
277			return 1;
278		}
279	}
280}
281
282/* read_pop():
283 *	Pop a macro from the stack
284 */
285static void
286read_pop(struct macros *ma)
287{
288	int i;
289
290	free(ma->macro[0]);
291	for (i = 0; i < ma->level; i++)
292		ma->macro[i] = ma->macro[i + 1];
293	ma->level--;
294	ma->offset = 0;
295}
296
297static void
298read_clearmacros(struct macros *ma)
299{
300	while (ma->level >= 0)
301		free(ma->macro[ma->level--]);
302	ma->offset = 0;
303}
304
305/* el_wgetc():
306 *	Read a wide character
307 */
308int
309el_wgetc(EditLine *el, wchar_t *cp)
310{
311	struct macros *ma = &el->el_read->macros;
312	int num_read;
313
314	terminal__flush(el);
315	for (;;) {
316		if (ma->level < 0)
317			break;
318
319		if (ma->macro[0][ma->offset] == '\0') {
320			read_pop(ma);
321			continue;
322		}
323
324		*cp = ma->macro[0][ma->offset++];
325
326		if (ma->macro[0][ma->offset] == '\0') {
327			/* Needed for QuoteMode On */
328			read_pop(ma);
329		}
330
331		return 1;
332	}
333
334	if (tty_rawmode(el) < 0)/* make sure the tty is set up correctly */
335		return 0;
336
337	num_read = (*el->el_read->read_char)(el, cp);
338
339	/*
340	 * Remember the original reason of a read failure
341	 * such that el_wgets() can restore it after doing
342	 * various cleanup operation that might change errno.
343	 */
344	if (num_read < 0)
345		el->el_read->read_errno = errno;
346
347	return num_read;
348}
349
350protected void
351read_prepare(EditLine *el)
352{
353	if (el->el_flags & HANDLE_SIGNALS)
354		sig_set(el);
355	if (el->el_flags & NO_TTY)
356		return;
357	if ((el->el_flags & (UNBUFFERED|EDIT_DISABLED)) == UNBUFFERED)
358		tty_rawmode(el);
359
360	/* This is relatively cheap, and things go terribly wrong if
361	   we have the wrong size. */
362	el_resize(el);
363	re_clear_display(el);	/* reset the display stuff */
364	ch_reset(el);
365	re_refresh(el);		/* print the prompt */
366
367	if (el->el_flags & UNBUFFERED)
368		terminal__flush(el);
369}
370
371protected void
372read_finish(EditLine *el)
373{
374	if ((el->el_flags & UNBUFFERED) == 0)
375		(void) tty_cookedmode(el);
376	if (el->el_flags & HANDLE_SIGNALS)
377		sig_clr(el);
378}
379
380const wchar_t *
381el_wgets(EditLine *el, int *nread)
382{
383	int retval;
384	el_action_t cmdnum = 0;
385	int num;		/* how many chars we have read at NL */
386	wchar_t wc;
387	wchar_t ch, *cp;
388	int crlf = 0;
389	int nrb;
390
391	if (nread == NULL)
392		nread = &nrb;
393	*nread = 0;
394	el->el_read->read_errno = 0;
395
396	if (el->el_flags & NO_TTY) {
397		size_t idx;
398
399		cp = el->el_line.buffer;
400		while ((num = (*el->el_read->read_char)(el, &wc)) == 1) {
401			*cp = wc;
402			/* make sure there is space for next character */
403			if (cp + 1 >= el->el_line.limit) {
404				idx = (cp - el->el_line.buffer);
405				if (!ch_enlargebufs(el, 2))
406					break;
407				cp = &el->el_line.buffer[idx];
408			}
409			cp++;
410			if (el->el_flags & UNBUFFERED)
411				break;
412			if (cp[-1] == '\r' || cp[-1] == '\n')
413				break;
414		}
415		if (num == -1 && errno == EINTR)
416			cp = el->el_line.buffer;
417		goto noedit;
418	}
419
420
421#ifdef FIONREAD
422	if (el->el_tty.t_mode == EX_IO && el->el_read->macros.level < 0) {
423		int chrs = 0;
424
425		(void) ioctl(el->el_infd, FIONREAD, &chrs);
426		if (chrs == 0) {
427			if (tty_rawmode(el) < 0) {
428				errno = 0;
429				*nread = 0;
430				return NULL;
431			}
432		}
433	}
434#endif /* FIONREAD */
435
436	if ((el->el_flags & UNBUFFERED) == 0)
437		read_prepare(el);
438
439	if (el->el_flags & EDIT_DISABLED) {
440		size_t idx;
441
442		if ((el->el_flags & UNBUFFERED) == 0)
443			cp = el->el_line.buffer;
444		else
445			cp = el->el_line.lastchar;
446
447		terminal__flush(el);
448
449		while ((num = (*el->el_read->read_char)(el, &wc)) == 1) {
450			*cp = wc;
451			/* make sure there is space next character */
452			if (cp + 1 >= el->el_line.limit) {
453				idx = (cp - el->el_line.buffer);
454				if (!ch_enlargebufs(el, 2))
455					break;
456				cp = &el->el_line.buffer[idx];
457			}
458			cp++;
459			crlf = cp[-1] == '\r' || cp[-1] == '\n';
460			if (el->el_flags & UNBUFFERED)
461				break;
462			if (crlf)
463				break;
464		}
465		if (num == -1 && errno == EINTR)
466			cp = el->el_line.buffer;
467		goto noedit;
468	}
469
470	for (num = -1; num == -1;) {  /* while still editing this line */
471		/* if EOF or error */
472		if (read_getcmd(el, &cmdnum, &ch) == -1)
473			break;
474		if ((int)cmdnum >= el->el_map.nfunc) /* BUG CHECK command */
475			continue;	/* try again */
476		/* now do the real command */
477		/* vi redo needs these way down the levels... */
478		el->el_state.thiscmd = cmdnum;
479		el->el_state.thisch = ch;
480		if (el->el_map.type == MAP_VI &&
481		    el->el_map.current == el->el_map.key &&
482		    el->el_chared.c_redo.pos < el->el_chared.c_redo.lim) {
483			if (cmdnum == VI_DELETE_PREV_CHAR &&
484			    el->el_chared.c_redo.pos != el->el_chared.c_redo.buf
485			    && iswprint(el->el_chared.c_redo.pos[-1]))
486				el->el_chared.c_redo.pos--;
487			else
488				*el->el_chared.c_redo.pos++ = ch;
489		}
490		retval = (*el->el_map.func[cmdnum]) (el, ch);
491
492		/* save the last command here */
493		el->el_state.lastcmd = cmdnum;
494
495		/* use any return value */
496		switch (retval) {
497		case CC_CURSOR:
498			re_refresh_cursor(el);
499			break;
500
501		case CC_REDISPLAY:
502			re_clear_lines(el);
503			re_clear_display(el);
504			/* FALLTHROUGH */
505
506		case CC_REFRESH:
507			re_refresh(el);
508			break;
509
510		case CC_REFRESH_BEEP:
511			re_refresh(el);
512			terminal_beep(el);
513			break;
514
515		case CC_NORM:	/* normal char */
516			break;
517
518		case CC_ARGHACK:	/* Suggested by Rich Salz */
519			/* <rsalz@pineapple.bbn.com> */
520			continue;	/* keep going... */
521
522		case CC_EOF:	/* end of file typed */
523			if ((el->el_flags & UNBUFFERED) == 0)
524				num = 0;
525			else if (num == -1) {
526				*el->el_line.lastchar++ = CONTROL('d');
527				el->el_line.cursor = el->el_line.lastchar;
528				num = 1;
529			}
530			break;
531
532		case CC_NEWLINE:	/* normal end of line */
533			num = (int)(el->el_line.lastchar - el->el_line.buffer);
534			break;
535
536		case CC_FATAL:	/* fatal error, reset to known state */
537			/* put (real) cursor in a known place */
538			re_clear_display(el);	/* reset the display stuff */
539			ch_reset(el);	/* reset the input pointers */
540			read_clearmacros(&el->el_read->macros);
541			re_refresh(el); /* print the prompt again */
542			break;
543
544		case CC_ERROR:
545		default:	/* functions we don't know about */
546			terminal_beep(el);
547			terminal__flush(el);
548			break;
549		}
550		el->el_state.argument = 1;
551		el->el_state.doingarg = 0;
552		el->el_chared.c_vcmd.action = NOP;
553		if (el->el_flags & UNBUFFERED)
554			break;
555	}
556
557	terminal__flush(el);		/* flush any buffered output */
558	/* make sure the tty is set up correctly */
559	if ((el->el_flags & UNBUFFERED) == 0) {
560		read_finish(el);
561		*nread = num != -1 ? num : 0;
562	} else {
563		*nread = (int)(el->el_line.lastchar - el->el_line.buffer);
564	}
565	goto done;
566noedit:
567	el->el_line.cursor = el->el_line.lastchar = cp;
568	*cp = '\0';
569	*nread = (int)(el->el_line.cursor - el->el_line.buffer);
570done:
571	if (*nread == 0) {
572		if (num == -1) {
573			*nread = -1;
574			if (el->el_read->read_errno)
575				errno = el->el_read->read_errno;
576		}
577		return NULL;
578	} else
579		return el->el_line.buffer;
580}
581