1 /* @(#)inputc.c	1.110 19/04/07 Copyright 1982, 1984-2019 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)inputc.c	1.110 19/04/07 Copyright 1982, 1984-2019 J. Schilling";
6 #endif
7 /*
8  *	inputc.c
9  *
10  *	characterwise input for bsh
11  *	line-editing
12  *	history using cursor-block
13  *
14  *	This is the second implementation that has been integrated into bsh.
15  *	It offeres exactly the same functionality as you could find in the
16  *	first implementation that was integrated into bsh and has been finished
17  *	around August 1984.
18  *
19  *	Both implementations are based on a simple shell prototype that I wrote
20  *	in 1982 and 1983. This prototype only contained the editor and called
21  *	shell commands via system().
22  *
23  *	Copyright (c) 1982, 1984-2019 J. Schilling
24  *	This version was first coded August 1984 and rewritten 01/22/85
25  *
26  *	Exported functions:
27  *		getinfile()	Return the current input FILE *
28  *		get_histlen()	Return the max. number of history entries
29  *		chghistory()	Change history size to string argument
30  *		init_input()	Init editor data structures
31  *		getnextc()	Noninterruptable getc() -> export to map.c
32  *		nextc()		Read mapped input from map.c (Input to inputc.c)
33  *		_nextwc()	Read mapped wide char input from map.c (Input
34  *				to inputc.c)
35  *		space()		Output n spaces
36  *		append_line()	Append a line into history (for hashcmd.c #lh)
37  *		match_hist()	Return matched history line for csh: !line
38  *		make_line()	Read a line from a file and return allocated
39  *				string
40  *		get_line()	-> Write Prompt, then return edited line
41  *		put_history()	Put out history to FILE *
42  *		save_history()	Save the history to ~/.history
43  *		read_init_history()	Initialize the history from ~/.history
44  *		readhistory()	Read history from a file
45  */
46 /*
47  * The contents of this file are subject to the terms of the
48  * Common Development and Distribution License, Version 1.0 only
49  * (the "License").  You may not use this file except in compliance
50  * with the License.
51  *
52  * See the file CDDL.Schily.txt in this distribution for details.
53  * A copy of the CDDL is also available via the Internet at
54  * http://www.opensource.org/licenses/cddl1.txt
55  *
56  * When distributing Covered Code, include this CDDL HEADER in each
57  * file and include the License file CDDL.Schily.txt from this distribution.
58  */
59 
60 #define	NO_SCHILY_STDIO_H	/* Try to avoid to #include schily/stdio.h */
61 #include <schily/string.h>
62 #include <schily/stdlib.h>
63 #include <schily/fcntl.h>
64 #include <schily/limits.h>	/* for  MB_LEN_MAX	*/
65 #include <schily/pwd.h>		/* #includes <stdio.h> */
66 #include <schily/wchar.h>	/* wchar_t #includes <stdio.h>	*/
67 #include <schily/wctype.h>	/* For iswprint()	*/
68 #include <schily/patmatch.h>	/* Past wchar.h to enable patchwmatch() */
69 #undef	NO_SCHILY_STDIO_H
70 #include <schily/stdio.h>	/* Past wchar.h to allow to redefine stdin */
71 #define	putch	dos_putch	/* Avoid DOS/curses putch() type clash */
72 #define	ungetch	dos_ungetch	/* Avoid DOS/curses ungetch() type clash */
73 #include <schily/termios.h>	/* For WIN-DOS test and USE_GETCH */
74 #undef	putch			/* Restore our old value */
75 #undef	ungetch			/* Restore our old value */
76 #include "bsh.h"
77 #include "map.h"
78 #include "node.h"
79 #include "str.h"
80 #include "strsubs.h"
81 #ifndef	USE_WCHAR	/* Use the system wctype.h if using wchars */
82 #define	REDEFINE_CTYPE
83 #include "ctype.h"
84 #endif	/* USE_WCHAR */
85 #undef	toint		/* Atari MiNT has this nonstandard definition */
86 #ifdef	LIB_SHEDIT
87 #define	toint		shell_toint
88 #define	MYFILE		int
89 #else
90 #define	MYFILE		FILE
91 #endif
92 
93 #ifdef	XDEBUG		/* eXpand Debug */
94 #define	DO_DEBUG
95 #else
96 #endif
97 
98 #ifdef	CWDEBUG		/* Clear word Debug */
99 #define	DO_DEBUG
100 #else
101 #endif
102 
103 #ifdef	USE_WCHAR
104 #ifndef	MB_LEN_MAX
105 #define	MB_LEN_MAX	10
106 #endif
107 #else	/* !USE_WCHAR */
108 #ifndef	MB_LEN_MAX
109 #define	MB_LEN_MAX	1
110 #endif
111 #undef	towupper
112 #define	towupper	toupper
113 #define	wcsrchr		strchr
114 #define	wcscmp		strcmp
115 #define	wcsncmp		strncmp
116 #define	wcscpy		strcpy
117 #define	wcslen		strlen
118 #define	wcsncpy		strncpy
119 #define	patwcompile	patcompile
120 #define	patwmatch	patmatch
121 #endif	/* !USE_WCHAR */
122 
123 #ifdef INTERACTIVE
124 
125 #define	_isdigit(c)	((c) >= '0' && (c) <= '9')
126 
127 /*
128  * The output buffer for the line editor.
129  * It uses multi byte characters.
130  */
131 #define	BUFSIZE	256
132 
133 typedef struct {
134 	int b_index;			/* Index in b_buf	*/
135 	char b_buf[BUFSIZE+MB_LEN_MAX];	/* The buffer space	*/
136 } BUF;
137 
138 #define	LINEQUANT	64
139 #define	BEGINLINE	1	/* ^A Move Cursor leftmost */
140 #define	SHOW		2	/* ^B Show possible file name completion(s) */
141 #define	CTRLC		3	/* ^C Logical INTR			    */
142 #define	CTRLD		4	/* ^D Logical EOF			    */
143 #define	ENDLINE		5	/* ^E Move Cursor rightmost		    */
144 #define	FORWARD		6	/* ^F Move Corsor one position to the right */
145 #define	BEEP		7	/* ^G Audible Bell			    */
146 #define	BACKSPACE	8	/* ^H Mode Cursor one position to the left  */
147 #define	TAB		9	/* ^I TAB is an alias for EXPAND	    */
148 				/* ^J New Line				    */
149 #define	DOWNWARD	14	/* ^N Scroll down to next line in history   */
150 #define	UPWARD		16	/* ^P Scroll up to previous line in history */
151 #define	RETYPE		18	/* ^R Redisplay current edit line	    */
152 #define	EXPAND		TAB	/* ^I Do file name completion		    */
153 #define	CTRLU		21	/* ^U Clear current edit line		    */
154 #define	CTRLV		22	/* ^V VNEXT quote next char		    */
155 #define	CTRLW		23	/* ^W Backwards erase one word		    */
156 #define	ESC		27	/* ^[ Lead in character for Escape sequence */
157 #define	QUOTECH		30	/* ^^ Quote next character		    */
158 #define	UNDO		31	/* ^_ Undo last remove operation	    */
159 #define	BLANK		' '
160 #define	BACKSLASH	'\\'
161 #define	DELETE		127	/* ^? Backwards erase one character	    */
162 
163 #define	BYTEMASK	0xFF
164 #define	SEARCH		0x100
165 #define	RESTORE		0x200
166 
167 #define	SEARCHUP	(SEARCH|UPWARD)
168 #define	SEARCHDOWN	(SEARCH|DOWNWARD)
169 
170 typedef struct histptr {
171 	struct histptr	*h_prev;	/* Previous element in revolver	    */
172 	struct histptr	*h_next;	/* Next element in revolver	    */
173 	wchar_t		*h_line;	/* Space to store the line	    */
174 	unsigned	h_len;		/* Number of wchar_t elements in line */
175 	unsigned	h_pos;		/* wchar_t based offset in line	    */
176 	unsigned	h_number;	/* Number used for POSIX "fc"	    */
177 	unsigned char	h_flags;	/* Flags, see below		    */
178 } _HISTPTR, *HISTPTR;
179 
180 /*
181  * Flags used in h_flags:
182  */
183 #define	F_TMP	0x01		/* Claimed by a tmp pointer */
184 
185 EXPORT	MYFILE	*getinfile	__PR((void));
186 EXPORT	int	get_histlen	__PR((void));
187 EXPORT	void	chghistory	__PR((char *cp));
188 LOCAL	void	changehistory	__PR((int n));
189 EXPORT	void	init_input	__PR((void));
190 EXPORT	int	getnextc	__PR((void));
191 EXPORT	int	nextc		__PR((void));
192 EXPORT	int	_nextwc		__PR((void));
193 LOCAL	void	writec		__PR((int c));
194 LOCAL	void	_writes		__PR((char *p));
195 LOCAL	void	writes		__PR((char *p));
196 LOCAL	void	_writews	__PR((wchar_t *p));
197 LOCAL	void	writews		__PR((wchar_t *p));
198 LOCAL	void	prettyp		__PR((int c, BUF * b));
199 LOCAL	void	bflush		__PR((void));
200 LOCAL	void	pch		__PR((int c, BUF * bp));
201 LOCAL	void	putch		__PR((int c));
202 LOCAL	void	beep		__PR((void));
203 LOCAL	int	chlen		__PR((int c));
204 LOCAL	int	linelen		__PR((wchar_t *s));
205 LOCAL	int	linediff	__PR((wchar_t *a, wchar_t *e));
206 LOCAL	void	backspace	__PR((int n));
207 EXPORT	void	space		__PR((int n));
208 LOCAL	void	delete		__PR((wchar_t *p));
209 LOCAL	wchar_t	*clearword	__PR((wchar_t *lp, wchar_t *cp,
210 						unsigned int *lenp));
211 LOCAL	void	clearline	__PR((wchar_t *lp, wchar_t *cp));
212 LOCAL	void	ins_char	__PR((wchar_t *cp, int c));
213 LOCAL	wchar_t	*ins_word	__PR((wchar_t *cp, wchar_t *s));
214 LOCAL	void	del_char	__PR((wchar_t *cp));
215 LOCAL	void	free_line	__PR((HISTPTR p));
216 LOCAL	HISTPTR	mktmp		__PR((void));
217 LOCAL	HISTPTR	hold_line	__PR((HISTPTR p));
218 LOCAL	void	unhold_line	__PR((HISTPTR p, HISTPTR op));
219 LOCAL	HISTPTR	remove_line	__PR((HISTPTR p));
220 EXPORT	void	append_line	__PR((char *linep, unsigned int len,
221 						unsigned int pos));
222 EXPORT	void	append_wline	__PR((wchar_t *linep, unsigned int len,
223 						unsigned int pos));
224 LOCAL	void	append_hline	__PR((wchar_t *linep, unsigned int len));
225 LOCAL	void	move_to_end	__PR((HISTPTR p));
226 LOCAL	void	stripout	__PR((void));
227 LOCAL	HISTPTR match_input	__PR((HISTPTR cur_line, HISTPTR tmp_line,
228 						BOOL up));
229 EXPORT	char	*match_hist	__PR((char *pattern));
230 LOCAL	HISTPTR match		__PR((HISTPTR cur_line, wchar_t *pattern,
231 						BOOL up));
232 LOCAL	int	edit_line	__PR((HISTPTR cur_line));
233 LOCAL	void	redisp		__PR((wchar_t *lp, wchar_t *cp));
234 LOCAL	wchar_t	*insert		__PR((wchar_t *cp, wchar_t *s,
235 						unsigned int *lenp));
236 LOCAL	wchar_t	*undo_del	__PR((wchar_t *lp, wchar_t *cp,
237 						unsigned int *lenp));
238 LOCAL	char	*strwchr	__PR((char *s, wchar_t c));
239 LOCAL	wchar_t	*xpwcs		__PR((wchar_t *cp, int qoff));
240 LOCAL	wchar_t *xp_tilde	__PR((char *ns, int *delp));
241 LOCAL	wchar_t	*xp_files	__PR((wchar_t *lp, wchar_t *cp, BOOL show,
242 						int *multip, int *delp));
243 LOCAL	wchar_t	*exp_files	__PR((wchar_t **lpp, wchar_t *cp,
244 						unsigned int *lenp,
245 						unsigned int *maxlenp,
246 						int *multip));
247 LOCAL	void	show_files	__PR((wchar_t *lp, wchar_t *cp));
248 LOCAL	int	get_request	__PR((void));
249 LOCAL	wchar_t	*esc_process	__PR((int xc, wchar_t *lp, wchar_t *cp,
250 						unsigned int *lenp));
251 LOCAL	wchar_t	*sget_line	__PR((void));
252 LOCAL	wchar_t	*iget_line	__PR((void));
253 EXPORT	char	*make_line	__PR((int (*f)(MYFILE *), MYFILE *arg));
254 LOCAL	char	*fread_line	__PR((MYFILE *f));
255 EXPORT	char	*get_line	__PR((int n, MYFILE *f));
256 EXPORT	int	put_history	__PR((MYFILE *f, int flg,
257 					int first, int last, char *subst));
258 EXPORT	HISTPTR	_search_history	__PR((int flg,
259 					int first, char *pat));
260 EXPORT	int	search_history	__PR((int flg,
261 					int first, char *pat));
262 LOCAL	char	*get_histfname	__PR((int flag));
263 EXPORT	void	save_history	__PR((int flg));
264 EXPORT	void	read_init_history	__PR((void));
265 EXPORT	void	readhistory	__PR((MYFILE *f));
266 LOCAL	void	term_init	__PR((void));
267 LOCAL	void	tty_init	__PR((void));
268 LOCAL	void	tty_term	__PR((void));
269 #ifdef	DO_DEBUG
270 LOCAL	void	cdbg		__PR((char *fmt, ...))	__printflike__(1, 2);
271 #endif
272 
273 extern	pid_t	mypgrp;
274 extern	int	delim;
275 extern	int	prflg;
276 extern	int	ttyflg;
277 extern	int	mapflag;
278 extern	BOOL	mp_init;
279 extern	char	*inithome;
280 extern	BOOL	ins_mode;
281 extern	BOOL	i_should_echo;
282 
283 LOCAL	MYFILE	*infile		= 0;			/* FILE * to read frm */
284 LOCAL	HISTPTR	first_line	= (HISTPTR) NULL;	/* Oldest line	    */
285 LOCAL	HISTPTR	last_line	= (HISTPTR) NULL;	/* Newest line in h   */
286 LOCAL	HISTPTR	rub_line	= (HISTPTR) NULL;
287 LOCAL	HISTPTR	del_line	= (HISTPTR) NULL;
288 LOCAL	char	*hfilename	= NULL;			/* Abs history path  */
289 LOCAL	char	*line_pointer	= NULL;			/* Temp MB line ptr  */
290 LOCAL	wchar_t	*wline_pointer	= NULL;			/* Temp WC line ptr  */
291 LOCAL	char	*iprompt	= NULL;			/* Current promot    */
292 LOCAL	int	histlen		= 0;			/* Max allowed h len */
293 LOCAL	int	no_lines	= 0;			/* Current hist len  */
294 LOCAL	BUF	buf		= {0};			/* BUF to optimize   */
295 LOCAL	char	mapesc		= '\0';			/* ESC char for map  */
296 LOCAL	unsigned hnumber	= 0;			/* For POSIX numbers */
297 #ifdef	notneeded
298 LOCAL	int	eof;
299 #endif
300 EXPORT	BOOL	(*__ign_eof)	__PR((void));
301 
302 
303 
304 /* Begin wide string support routines -------> */
305 EXPORT	wchar_t	*makewstr	__PR((wchar_t *s));
306 EXPORT wchar_t *
makewstr(s)307 makewstr(s)
308 	register	wchar_t *s;
309 {
310 			wchar_t	*tmp;
311 	register	wchar_t	*s1;
312 
313 	if ((tmp = malloc((size_t)((wcslen(s)+1)) * sizeof (wchar_t))) ==
314 	    NULL) {
315 		raisecond("makewstr", (long)NULL);
316 		return (0);
317 	}
318 	for (s1 = tmp; (*s1++ = *s++) != '\0'; );
319 	return (tmp);
320 }
321 
322 LOCAL wchar_t *towcs	__PR((wchar_t *wbuf, size_t bsize, char *s,
323 							ssize_t len));
324 LOCAL wchar_t *
towcs(wbuf,bsize,s,len)325 towcs(wbuf, bsize, s, len)
326 	wchar_t	*wbuf;
327 	size_t	bsize;
328 	char	*s;
329 	ssize_t	len;
330 {
331 	int	i;
332 	int	mlen;
333 	int	chars;
334 	char	*sp;
335 	wchar_t	*wp;
336 	wchar_t	c;
337 
338 	if (len < 0)
339 		len = strlen(s);
340 
341 	(void) mbtowc(NULL, NULL, 0);
342 	for (sp = s, i = len, chars = 0; i > 0 && *sp != '\0'; ) {
343 		mlen = mbtowc(&c, sp, i);
344 		if (mlen <= 0) {
345 			(void) mbtowc(NULL, NULL, 0);
346 			mlen = 1;
347 		}
348 		i -= mlen;
349 		sp += mlen;
350 		chars++;
351 	}
352 	i -= 1;
353 	chars++;
354 
355 	if (wbuf == NULL || chars > bsize) {
356 		wbuf = malloc(chars * sizeof (wchar_t));
357 		if (wbuf == NULL)
358 			return (wbuf);
359 	}
360 
361 	(void) mbtowc(NULL, NULL, 0);
362 	for (wp = wbuf, sp = s, i = len; i > 0 && *sp != '\0'; wp++) {
363 		mlen = mbtowc(&c, sp, i);
364 		if (mlen <= 0) {
365 			(void) mbtowc(NULL, NULL, 0);
366 			mlen = 1;
367 			c = *sp & 0xFF;
368 		}
369 		*wp = c;
370 		sp += mlen;
371 		i -= mlen;
372 	}
373 	*wp = 0;
374 	return (wbuf);
375 }
376 
377 LOCAL char *tombs	__PR((char *cbuf, size_t bsize, wchar_t *ws,
378 						ssize_t wlen));
379 LOCAL char *
tombs(cbuf,bsize,ws,wlen)380 tombs(cbuf, bsize, ws, wlen)
381 	char	*cbuf;
382 	size_t	bsize;
383 	wchar_t	*ws;
384 	ssize_t	wlen;
385 {
386 	int	len;
387 	wchar_t	*wcp;
388 	char	*cp;
389 	char	cstr[MB_LEN_MAX];
390 
391 	if (wlen < 0)
392 		wlen = wcslen(ws);
393 
394 	for (len = 0, wcp = ws; *wcp; wcp++) {
395 		int	mlen;
396 
397 		mlen = wctomb(cstr, *wcp);
398 		if (mlen <= 0)
399 			mlen = 1;
400 		len += mlen;
401 	}
402 	len++;
403 
404 	if (cbuf == NULL || len > bsize) {
405 		cbuf = malloc(len);
406 		if (cbuf == NULL)
407 			return (cbuf);
408 	}
409 
410 	for (wcp = ws, cp = cbuf; *wcp; wcp++) {
411 		int	mlen;
412 
413 		mlen = wctomb(cp, *wcp);
414 		if (mlen <= 0) {
415 			mlen = 1;
416 			*cp = *wcp & 0xFF;
417 			if (*cp == '\0')
418 				*cp = '?';
419 		}
420 		cp += mlen;
421 	}
422 	*cp = '\0';
423 	return (cbuf);
424 }
425 /* -------> End wide string support routines */
426 
427 
428 /*
429  * Return actual input FILE *
430  */
431 EXPORT MYFILE *
getinfile()432 getinfile()
433 {
434 	return (infile);
435 }
436 
437 
438 /*
439  * Return the actual length of the history.
440  */
441 EXPORT int
get_histlen()442 get_histlen()
443 {
444 	return (histlen);
445 }
446 
447 
448 /*
449  * Read new value and change the length of the current history to this value.
450  * chghistory() gets called when the environment is changed for HISTORY=
451  */
452 EXPORT void
chghistory(cp)453 chghistory(cp)
454 	char	*cp;	/* A string with the new value of the history length */
455 {
456 	int	n;
457 
458 #ifdef DEBUG
459 	printf("chghistory ('%s')\r\n", cp);
460 #endif
461 	if (!toint(gstd, cp, &n) || n < 0) {
462 		berror("Bad value '%s' for %s.", cp, histname);
463 		return;
464 	}
465 	changehistory(n);
466 }
467 
468 
469 /*
470  * Change the length of the current history.
471  */
472 LOCAL void
changehistory(n)473 changehistory(n)
474 	register int	n;
475 {
476 	while (n < no_lines)
477 		remove_line(first_line);
478 	histlen = n;
479 }
480 
481 EXPORT void
histrange(firstp,lastp,nextp)482 histrange(firstp, lastp, nextp)
483 	unsigned	*firstp;
484 	unsigned	*lastp;
485 	unsigned	*nextp;
486 {
487 	/*
488 	 * Make it safe against a state where the history is not yet initialized
489 	 * and first_line or last_line are still NULL pointers.
490 	 */
491 	if (firstp)
492 		*firstp = first_line == 0 ? 0 : first_line->h_number;
493 	if (lastp)
494 		*lastp = last_line == 0 ? 0 : last_line->h_number;
495 	if (nextp)					/* XXX */
496 		*nextp = (hnumber + 1) ? hnumber + 1 : hnumber + 2;
497 }
498 
499 /*
500  * Init the history to the default size.
501  */
502 EXPORT void
init_input()503 init_input()
504 {
505 	register char	*p;
506 
507 #ifdef DEBUGX
508 	printf("init_input()\r\n");
509 #endif
510 	/*
511 	 * First check for "HISTSIZE" as this is POSIX
512 	 */
513 	if ((p = getcurenv(histsizename)) != NULL) {
514 		/* EMPTY */;
515 	} else if ((p = getcurenv(histname)) == NULL) {
516 		/*
517 		 * If our historic name "HISTORY" is not present, use the
518 		 * minimum size required by POSIX.
519 		 */
520 		p = "128";
521 		ev_insert(concat(histname, eql, p, (char *)NULL));
522 	}
523 	chghistory(p);
524 	rub_line = mktmp();
525 	del_line = mktmp();
526 }
527 
528 
529 /*
530  * Non interruptable version of getc() from stdio lib.
531  */
532 EXPORT int
getnextc()533 getnextc()
534 {
535 	int	c;
536 	int	errs = 0;
537 
538 again:
539 #ifdef	USE_GETCH
540 	c = getch();	/* DOS console input */
541 #else
542 	c = getc(infile);
543 #endif
544 	if (c < 0 && geterrno() == EINTR) {
545 		clearerr(infile);
546 		if (++errs < 10)
547 			goto again;
548 	}
549 
550 #if	defined(F_GETFL) && defined(O_NONBLOCK) && \
551 	(defined(EAGAIN) || defined(EWOULDBLOCK))
552 #ifndef	EWOULDBLOCK			/* Not present on DJGPP */
553 #define	EWOULDBLOCK	EAGAIN
554 #endif
555 #ifndef	EAGAIN				/* Make it symmetric */
556 #define	EAGAIN		EWOULDBLOCK
557 #endif
558 	if (c < 0 && (geterrno() == EAGAIN || geterrno() == EWOULDBLOCK)) {
559 		int	fl;
560 
561 		fl = fcntl(fdown(infile), F_GETFL, 0);
562 		fl &= ~O_NONBLOCK;
563 		fcntl(fdown(infile), F_SETFL, fl);
564 		clearerr(infile);
565 		if (++errs < 10)
566 			goto again;
567 	}
568 #endif
569 	return (c);
570 }
571 
572 
573 /*
574  * Get next character from mapper.
575  * This allows us to implement cursor key mappings.
576  */
577 EXPORT int
nextc()578 nextc()
579 {
580 	register int	c;
581 
582 	if (!mapflag) {
583 		if ((c = mapgetc()) == mapesc)
584 			c = mapgetc();
585 		else if (rmap(c))
586 			c = gmap();
587 	} else if ((c = gmap()) == 0) {
588 		c = nextc();
589 	}
590 	return (c);
591 }
592 
593 /*
594  * Get next wide character
595  * Caution: The Bourne Shell has nextwc() in word.c
596  */
597 EXPORT int
_nextwc()598 _nextwc()
599 {
600 	register int	cur_max = MB_CUR_MAX;
601 	register int	i;
602 	int		mlen;
603 	wchar_t		c = -1;
604 	char		cstr[MB_LEN_MAX];
605 	char		*cp;
606 	int		ic;
607 
608 	(void) mbtowc(NULL, NULL, 0);
609 	for (i = 1, cp = cstr; i <= cur_max; i++) {
610 		ic = nextc();
611 		if (ic == EOF)
612 			break;
613 		*cp++ = (char)ic;
614 		mlen = mbtowc(&c, cstr, i);
615 		if (mlen >= 0)
616 			break;
617 		(void) mbtowc(NULL, NULL, 0);
618 	}
619 #ifdef	_NEXTWC_DEBUG
620 	error("C %d %x\n", c, c);
621 #endif
622 	return ((int)c);
623 }
624 
625 
626 /*
627  * Write a single (wide) character to our tty
628  */
629 LOCAL void
writec(c)630 writec(c)
631 	int	c;
632 {
633 	prettyp(c, &buf);
634 }
635 
636 
637 /*
638  * Write a string
639  * Needed for internal string output like "<EOF>"
640  */
641 LOCAL void
_writes(p)642 _writes(p)
643 	register char	*p;
644 {
645 	register BUF	*rb = &buf;
646 
647 	while (*p)
648 		prettyp(*p++, rb);
649 }
650 
651 LOCAL void
writes(p)652 writes(p)
653 	char	*p;
654 {
655 	_writes(p);
656 	bflush();
657 }
658 
659 /*
660  * Write a wide character string
661  */
662 LOCAL void
_writews(p)663 _writews(p)
664 	register wchar_t	*p;
665 {
666 	register BUF	*rb = &buf;
667 
668 	while (*p)
669 		prettyp(*p++, rb);
670 }
671 
672 LOCAL void
writews(p)673 writews(p)
674 	wchar_t	*p;
675 {
676 	_writews(p);
677 	bflush();
678 }
679 
680 
681 /*
682  * Write a single (wide) character in expanded (readable) form.
683  * Non printable characters are frefixed by '~' if they
684  * are beyond 0x7F and prefixed by '^' if they are a
685  * control character.
686  */
687 LOCAL void
prettyp(c,b)688 prettyp(c, b)
689 	register int	c;
690 	register BUF	*b;
691 {
692 	if (iswprint(c) /* || c == '\n' */) {
693 		pch(c, b);
694 		return;
695 	}
696 	if (c > 0xFF) {
697 		char	s[16];
698 
699 		js_snprintf(s, sizeof (s), "'%10.10X", c);
700 		writes(s);
701 		return;
702 	}
703 	if (c & 0x80) {
704 		pch('~', b);
705 		prettyp(c & 0177, b);
706 	} else {
707 		pch('^', b);
708 		pch(c ^ 0100, b);
709 	}
710 }
711 
712 
713 /*
714  * Flush our internal line buffering module.
715  */
716 LOCAL void
bflush()717 bflush()
718 {
719 	register BUF *bp = &buf;
720 
721 	if (bp->b_index <= 0)
722 		return;
723 	(void) filewrite(stdout, bp->b_buf, bp->b_index);
724 	(void) fflush(stdout);
725 	bp->b_index = 0;
726 }
727 
728 
729 /*
730  * Put a (wide) character into out line buffering module.
731  */
732 LOCAL void
pch(c,bp)733 pch(c, bp)
734 		int	c;
735 	register BUF	*bp;
736 {
737 		char	cstr[MB_LEN_MAX];
738 		int	len;
739 
740 	if (bp->b_index >= BUFSIZE)
741 		bflush();
742 
743 	len = wctomb(cstr, (wchar_t)c);
744 	if (len <= 0) {
745 		if ((c & 0xFF) == '\0')
746 			c = '?';
747 
748 		bp->b_buf[bp->b_index++] = (char)c;
749 	} else {
750 		char	*cp = cstr;
751 
752 		while (--len >= 0) {
753 			bp->b_buf[bp->b_index++] = *cp++;
754 		}
755 		if (bp->b_index >= BUFSIZE)	/* If >= BUFSIZE	*/
756 			bflush();		/* flush immediately    */
757 	}
758 }
759 
760 
761 /*
762  * Simple (wide) putchar() replacement that uses our line buffering.
763  */
764 LOCAL void
putch(c)765 putch(c)
766 	int	c;
767 {
768 	pch(c, &buf);
769 }
770 
771 
772 /*
773  * Ring the bell.
774  */
775 LOCAL void
beep()776 beep()
777 {
778 	char	*p = getcurenv("BEEP");
779 
780 	if (p != NULL && streql(p, "off"))
781 		return;
782 	putch(BEEP);
783 	bflush();
784 }
785 
786 
787 /*
788  * Compute the visible length of a (wide) character.
789  */
790 LOCAL int
chlen(c)791 chlen(c)
792 	register int	c;
793 {
794 	if (iswprint(c) /* || c == '\n' */) {
795 #ifdef	HAVE_WCWIDTH
796 		return (wcwidth(c));
797 #else
798 		return (1);
799 #endif
800 	}
801 	if (c > 0xFF)		/* UNICODE	*/
802 		return (11);	/* "'%10.10X"	*/
803 	if (c & 0x80)
804 		return (2 + !iswprint(c&0177));
805 	return (2);
806 }
807 
808 
809 /*
810  * Compute the visible length of a wide string.
811  */
812 LOCAL int
linelen(s)813 linelen(s)
814 	register wchar_t	*s;
815 {
816 	register int	len = 0;
817 	while (*s)
818 		len += chlen(*s++);
819 	return (len);
820 }
821 
822 
823 /*
824  * Compute the visible difference of two characters in a string.
825  */
826 LOCAL int
linediff(a,e)827 linediff(a, e)
828 	register wchar_t	*a;
829 	register wchar_t	*e;
830 {
831 	register int	diff = 0;
832 
833 	while (*a && a < e)
834 		diff += chlen(*a++);
835 	return (diff);
836 }
837 
838 
839 /*
840  * Back space cursor by 'n'.
841  */
842 LOCAL void
backspace(n)843 backspace(n)
844 	register int	n;
845 {
846 	register BUF	*rb = &buf;
847 
848 	while (n--)
849 		pch(BACKSPACE, rb);
850 	bflush();
851 }
852 
853 
854 /*
855  * Forward space cursor by 'n'. Do this by writing spaces.
856  */
857 EXPORT void
space(n)858 space(n)
859 	register int	n;
860 {
861 	register BUF	*rb = &buf;
862 
863 	while (n--)
864 		pch(BLANK, rb);
865 	bflush();
866 }
867 
868 
869 /*
870  * loescht bis Ende der Zeile
871  */
872 LOCAL void
delete(p)873 delete(p)
874 	register wchar_t	*p;
875 {
876 	register int	len = 0;
877 
878 	while (*p)
879 		len += chlen(*p++);
880 	space(len);
881 	backspace(len);
882 }
883 
884 /*
885  * loescht ein Wort rueckwaerts wie im Terminaltreiber mit ^W
886  */
887 LOCAL wchar_t *
clearword(lp,cp,lenp)888 clearword(lp, cp, lenp)
889 	register wchar_t	*lp;
890 	register wchar_t	*cp;
891 	unsigned		*lenp;
892 {
893 	register int		dl	= 0;	/* Displayed len of word */
894 	register int		wl;		/* Length of word in chars */
895 	register wchar_t	*p;
896 		wchar_t		*sp	= cp;
897 
898 	if (cp == lp) {				/* At begin of line */
899 		beep();
900 		return (cp);
901 	}
902 	cp--;
903 	while (cp >= lp && *cp == BLANK) {	/* Skip space behind word */
904 		--cp, dl++;
905 	}
906 	while (cp >= lp && *cp != BLANK)	/* Skip chars in word	*/
907 		dl += chlen(*cp--);
908 	backspace(dl);				/* Backspace over del chars */
909 	cp++;
910 
911 	wl = sp - cp;
912 
913 	for (p = cp; ; p++) {
914 		if ((*p = *(p+wl)) == '\0')
915 			break;
916 	}
917 	writews(cp);				/* Write new end of line */
918 	space(dl);				/* Overwrite deleted space */
919 	backspace(linelen(cp) + dl);		/* Readjust cursor position */
920 
921 
922 	*lenp -= wl;
923 	return (cp);
924 }
925 
926 /*
927  * loescht die ganze Zeile
928  */
929 LOCAL void
clearline(lp,cp)930 clearline(lp, cp)
931 	wchar_t	*lp;
932 	wchar_t	*cp;
933 {
934 	backspace(linediff(lp, cp));
935 	delete(lp);
936 }
937 
938 /*
939  * Fuegt char c bei cp ein.
940  */
941 LOCAL void
ins_char(cp,c)942 ins_char(cp, c)
943 	register wchar_t	*cp;
944 		int		c;
945 {
946 	register wchar_t	*ep = cp;
947 
948 	writews(cp);
949 	backspace(linelen(cp));
950 	while (*ep++);
951 	ep--;
952 	do {
953 		*(ep + 1) = *ep;
954 	} while (ep-- > cp);
955 	*cp = c;
956 }
957 
958 
959 /*
960  * Fuegt String s bei cp ein.
961  */
962 LOCAL wchar_t *
ins_word(cp,s)963 ins_word(cp, s)
964 	register wchar_t	*cp;
965 	register wchar_t	*s;
966 {
967 	register wchar_t	*ep = cp;
968 	register int		len;
969 
970 	len = wcslen(s);
971 	writews(cp);
972 	backspace(linelen(cp));
973 	while (*ep++);
974 	ep--;
975 	do {
976 		*(ep + len) = *ep;
977 	} while (ep-- > cp);
978 	while (*s)
979 		*cp++ = *s++;
980 	return (cp);
981 }
982 
983 
984 /*
985  * loescht Zeichen *cp und stellt dar
986  */
987 LOCAL void
del_char(cp)988 del_char(cp)
989 	register wchar_t	*cp;
990 {
991 	register wchar_t	*p;
992 	register int		dl = 0;
993 
994 	backspace(dl = chlen(*cp));
995 	for (p = cp; *p; p++)
996 		*p = *(p+1);
997 	writews(cp);
998 	space(dl);
999 	backspace(linelen(cp) + dl);
1000 }
1001 
1002 #ifdef	OOO
1003 LOCAL char *
new_line(lp,old,new)1004 new_line(lp, old, new)
1005 	char		*lp;
1006 	unsigned	old;
1007 	unsigned	new;
1008 {
1009 	register char	*np;
1010 
1011 		/* realloc !!! */
1012 	np = malloc(new * sizeof (*lp));
1013 	if (np == NULL)
1014 		return (np);
1015 	movebytes(lp, np, (int)old);	/* make_line hat kein NULL Byte ! */
1016 	free(lp);
1017 	return (np);
1018 }
1019 #else
1020 #define	new_line(lp, old, new)	realloc(lp, new * sizeof (*lp))
1021 #endif
1022 
1023 /*
1024  * Free a history line Tnode.
1025  */
1026 LOCAL void
free_line(p)1027 free_line(p)
1028 	register HISTPTR	p;
1029 {
1030 	p->h_flags = 0;
1031 	free(p->h_line);
1032 	free((char *)p);
1033 }
1034 
1035 /*
1036  * Create a temporary history line template.
1037  */
1038 LOCAL HISTPTR
mktmp()1039 mktmp()
1040 {
1041 	register HISTPTR	tmp;
1042 
1043 	tmp = (HISTPTR)malloc(sizeof (_HISTPTR));
1044 	if (tmp == NULL)
1045 		return (tmp);
1046 	tmp->h_prev = tmp->h_next = NULL;
1047 	tmp->h_line = malloc(LINEQUANT * sizeof (*tmp->h_line));
1048 	if (tmp->h_line == NULL) {
1049 		free(tmp);
1050 		return ((HISTPTR) NULL);
1051 	}
1052 	tmp->h_len = LINEQUANT;
1053 	tmp->h_pos = 0;
1054 	tmp->h_number = 0;
1055 	tmp->h_flags = F_TMP;		/* Mark it temporary */
1056 	tmp->h_line[0] = '\0';
1057 	return (tmp);
1058 }
1059 
1060 /*
1061  * Create an editable copy of a history line.
1062  */
1063 LOCAL HISTPTR
hold_line(p)1064 hold_line(p)
1065 	register HISTPTR	p;
1066 {
1067 	register HISTPTR	tmp;
1068 
1069 	tmp = (HISTPTR)malloc(sizeof (_HISTPTR));
1070 	if (tmp == NULL)
1071 		return (tmp);
1072 	tmp->h_prev = p->h_prev;
1073 	tmp->h_next = p->h_next;
1074 	tmp->h_line = malloc(p->h_len * sizeof (*tmp->h_line));
1075 	if (tmp->h_line == NULL) {
1076 		free(tmp);
1077 		return ((HISTPTR) NULL);
1078 	}
1079 	tmp->h_len = p->h_len;
1080 	tmp->h_pos = p->h_pos;
1081 	tmp->h_number = 0;
1082 	tmp->h_flags = F_TMP;		/* Mark it temporary */
1083 	movebytes(p->h_line, tmp->h_line, p->h_len * sizeof (*tmp->h_line));
1084 	return (tmp);
1085 }
1086 
1087 LOCAL void
unhold_line(p,op)1088 unhold_line(p, op)
1089 	register HISTPTR	p;	/* copy of hold line */
1090 	register HISTPTR	op;	/* original hold line */
1091 {
1092 	if (p == (HISTPTR) NULL)
1093 		return;
1094 
1095 	if (op) {
1096 		int len = wcslen(op->h_line);
1097 
1098 		if (p->h_pos > len)
1099 			op->h_pos = len;
1100 		else
1101 			op->h_pos = p->h_pos;
1102 	}
1103 	if ((p->h_flags & F_TMP) != 0)
1104 		free_line(p);
1105 }
1106 
1107 #ifdef DEBUG
1108 LOCAL void
trace_hist()1109 trace_hist()
1110 {
1111 	register HISTPTR	p;
1112 
1113 	for (p = first_line; p; p = p->h_next) {
1114 		printf("{ %s } at %p, prev at %p, next at %p\r\n",
1115 			p->h_line, p, p->h_prev, p->h_next);
1116 	}
1117 }
1118 #endif
1119 
1120 
1121 /*
1122  * Remove a line from the history list.
1123  */
1124 LOCAL HISTPTR
remove_line(p)1125 remove_line(p)
1126 	register HISTPTR	p;
1127 {
1128 	register HISTPTR	lp;
1129 	register HISTPTR	np;
1130 
1131 	if (p == (HISTPTR) NULL)
1132 		return ((HISTPTR) NULL);
1133 #ifdef DEBUG
1134 	printf("removing line at %p, chars at %p\r\n", p, p->h_line);
1135 #endif
1136 	lp = p->h_prev;
1137 	np = p->h_next;
1138 	if ((p->h_flags & F_TMP) == 0) {
1139 		/*
1140 		 * Only free it if not claimed by a temporary pointer.
1141 		 */
1142 		free(p->h_line);
1143 		free((char *)p);
1144 	}
1145 	if (lp)
1146 		lp->h_next = np;
1147 	else
1148 		first_line = np;
1149 	if (np)
1150 		np->h_prev = lp;
1151 	else
1152 		last_line = lp;
1153 	no_lines--;
1154 #ifdef DEBUG
1155 	printf("removed. first at %p, last at %p, no_lines = %d\r\n",
1156 		first_line, last_line, no_lines);
1157 #endif
1158 	if (lp == (HISTPTR) NULL)
1159 		lp = first_line;
1160 	return (lp);
1161 }
1162 
1163 
1164 /*
1165  * Append a multibyte line to the end of the history list.
1166  */
1167 EXPORT void
append_line(linep,len,pos)1168 append_line(linep, len, pos)
1169 	char		*linep;
1170 	unsigned	len;
1171 	unsigned	pos;
1172 {
1173 	wchar_t		wline[512];
1174 	wchar_t		*wp;
1175 
1176 	wp = towcs(wline, sizeof (wline) / sizeof (wline[0]), linep, len);
1177 	if (wp == NULL)
1178 		return;
1179 
1180 	len = 1 + wcslen(wp);
1181 	append_wline(wp, len, pos);
1182 
1183 	if (wp != wline)
1184 		free(wp);
1185 }
1186 
1187 /*
1188  * Append a wide character line to the end of the history list.
1189  */
1190 EXPORT void
append_wline(linep,len,pos)1191 append_wline(linep, len, pos)
1192 	wchar_t		*linep;
1193 	unsigned	len;
1194 	unsigned	pos;
1195 {
1196 	register HISTPTR	p;
1197 	register wchar_t	*lp;
1198 
1199 #ifdef DEBUG
1200 	printf("appending line, histlen = %d.\r\n", histlen);
1201 #endif
1202 	if (histlen == 0)
1203 		return;
1204 	if (no_lines == histlen)
1205 		remove_line(first_line);
1206 	p = (HISTPTR)malloc(sizeof (_HISTPTR));
1207 	if (p == NULL)
1208 		return;
1209 	lp = malloc(len * sizeof (*lp));
1210 	if (lp == NULL) {		/* Do nothing if malloc() fails */
1211 		free(p);
1212 		return;
1213 	}
1214 	wcscpy(lp, linep);
1215 	p->h_prev = last_line;
1216 	p->h_line = lp;
1217 	p->h_len  = len;
1218 	p->h_pos  = pos;
1219 	p->h_number = ++hnumber;		/* XXX */
1220 	if (p->h_number == 0)
1221 		p->h_number = ++hnumber;
1222 	p->h_flags = 0;
1223 	p->h_next = (HISTPTR) NULL;
1224 	if (last_line)
1225 		last_line->h_next = p;
1226 	else
1227 		first_line = p;
1228 	last_line = p;
1229 	no_lines++;
1230 #ifdef DEBUG
1231 	printf("appended line: first at %p, last at %p, no_lines = %d\r\n",
1232 		first_line, last_line, no_lines);
1233 #endif
1234 }
1235 
1236 
1237 /*
1238  * Provisorisch Folgezeilen in History (fuer if then else ... History)
1239  *
1240  */
1241 LOCAL void
append_hline(linep,len)1242 append_hline(linep, len)
1243 	wchar_t	*linep;
1244 	unsigned len;
1245 {
1246 	register HISTPTR	p = last_line;
1247 	register wchar_t	*lp;
1248 		wchar_t		anl[2];
1249 
1250 	len += p->h_len;
1251 	lp = malloc(len * sizeof (*lp));
1252 	if (lp == NULL)		/* Do nothing if malloc() fails */
1253 		return;
1254 #ifdef	USE_ANSI_NL_SEPARATOR
1255 	anl[0] = '\205';
1256 #else
1257 	anl[0] = '\n';
1258 #endif
1259 	anl[1] = '\0';
1260 	wcscatl(lp, p->h_line, anl, linep, (wchar_t *)NULL);
1261 	free(p->h_line);
1262 	p->h_line = lp;
1263 	p->h_len  = len;
1264 	/*
1265 	 * Keep cursor position of first line.
1266 	 */
1267 }
1268 
1269 
1270 /*
1271  * Move line p to the end of the history.
1272  */
1273 LOCAL void
move_to_end(p)1274 move_to_end(p)
1275 	HISTPTR p;
1276 {
1277 	register HISTPTR lp;
1278 	register HISTPTR np;
1279 
1280 	if ((np = p->h_next) != NULL) {
1281 		lp = p->h_prev;
1282 		if (lp)	{
1283 			/* middle of history */
1284 			lp->h_next = np;
1285 			np->h_prev = lp;
1286 		} else {
1287 			/* first line of history */
1288 			np->h_prev = (HISTPTR) NULL;
1289 			first_line = np;
1290 		}
1291 		last_line->h_next = p;
1292 		p->h_prev = last_line;
1293 		p->h_next = (HISTPTR) NULL;
1294 		p->h_number = ++hnumber;	/* XXX */
1295 		if (p->h_number == 0)
1296 			p->h_number = ++hnumber;
1297 		p->h_flags &= ~F_TMP;
1298 		last_line = p;
1299 	}
1300 #ifdef DEBUG
1301 	printf("moved line: first at %p, last at %p\r\n",
1302 						first_line, last_line);
1303 #endif
1304 }
1305 
1306 
1307 /*
1308  * Stripout entfernt alle Zeilen, die den gleichen Inhalt wie die lezte Zeile
1309  * haben. Stripout wird aufgerufen, nachdem die aktuell editierte Zeile die
1310  * lezte Zeile geworden ist.
1311  */
1312 LOCAL void
stripout()1313 stripout()
1314 {
1315 	register HISTPTR	p;
1316 	register wchar_t	*linep;
1317 
1318 	if (!last_line)
1319 		return;
1320 	linep = last_line->h_line;
1321 #ifdef DEBUG
1322 	printf("stripping out '%s'.\r\n", linep);
1323 #endif
1324 	for (p = first_line; p && p != last_line; p = p->h_next) {
1325 #ifdef DEBUG
1326 		printf("stripout: '%s'.\r\n", p->h_line);
1327 #endif
1328 		if (wcslen(p->h_line) == 0 || wcseql(linep, p->h_line)) {
1329 			p = remove_line(p);
1330 			if (p == (HISTPTR)NULL)
1331 				break;
1332 		}
1333 	}
1334 }
1335 
1336 
1337 /*
1338  * Implement the bsh history search function.
1339  */
1340 LOCAL HISTPTR
match_input(cur_line,tmp_line,up)1341 match_input(cur_line, tmp_line, up)
1342 	HISTPTR	cur_line;
1343 	HISTPTR	tmp_line;
1344 	BOOL	up;
1345 {
1346 	static	HISTPTR	match_line = (HISTPTR) NULL;
1347 		HISTPTR	hp;
1348 		char		*oldprompt;
1349 		wchar_t		*pattern;
1350 
1351 	if (!match_line)
1352 		match_line = mktmp();
1353 	if (!match_line) {
1354 		(void) fprintf(stderr, "\r\n");
1355 		(void) fflush(stderr);
1356 		beep();
1357 		hp = tmp_line;
1358 		goto fail;
1359 	}
1360 	oldprompt = iprompt;
1361 	iprompt = up ? "search up: " : "search down: ";
1362 	(void) fprintf(stderr, "\r\n%s", iprompt);
1363 	(void) fflush(stderr);
1364 	for (;;)
1365 		if (edit_line(match_line) == '\n')
1366 			break;
1367 		else
1368 			beep();
1369 	pattern = match_line->h_line;
1370 	if ((hp = match(cur_line, pattern, up)) == (HISTPTR) NULL)
1371 		hp = tmp_line;
1372 	iprompt = oldprompt;
1373 fail:
1374 	(void) fprintf(stderr, "%s", iprompt);
1375 	(void) fflush(stderr);
1376 	return (hp);
1377 }
1378 
1379 
1380 /*
1381  * Implement the !pattern search that is a simple csh feature.
1382  * match_hist() is called by the parser when !<pattern> is seen.
1383  */
1384 EXPORT char *
match_hist(pattern)1385 match_hist(pattern)
1386 		char		*pattern;
1387 {
1388 	register HISTPTR	hp;
1389 		wchar_t		wpattern[32];
1390 		wchar_t		*wp;
1391 
1392 	wpattern[0] = '\0';
1393 	if (streql(pattern, "!")) {
1394 		wpattern[0] = '*';
1395 		wpattern[1] = '\0';
1396 		wp = wpattern;
1397 	} else {
1398 		wp = towcs(wpattern, sizeof (wpattern) / sizeof (wpattern[0]),
1399 								pattern, -1);
1400 		if (wp == NULL)
1401 			return (NULL);
1402 	}
1403 	remove_line(last_line);
1404 	if ((hp = match(last_line, wp, TRUE)) == (HISTPTR) NULL) {
1405 		if (wp != wpattern)
1406 			free(wp);
1407 		return (NULL);
1408 	}
1409 	if (wp != wpattern)
1410 		free(wp);
1411 	move_to_end(hp);
1412 
1413 	if (line_pointer)
1414 		free(line_pointer);
1415 	line_pointer = tombs(NULL, 0, hp->h_line, -1);
1416 
1417 	if (line_pointer)
1418 		(void) fprintf(stderr, "%s\r\n", line_pointer);
1419 	(void) fflush(stderr);
1420 	return (line_pointer);
1421 }
1422 
1423 
1424 /*
1425  * Find a line that matches pattern in history.
1426  */
1427 LOCAL HISTPTR
match(cur_line,pattern,up)1428 match(cur_line, pattern, up)
1429 		HISTPTR	cur_line;
1430 	register wchar_t	*pattern;
1431 	register BOOL		up;
1432 {
1433 	register int		patlen;
1434 	register int		alt = 0;
1435 	register int		*aux;
1436 	register int		*state;
1437 	register HISTPTR	hp = (HISTPTR) NULL;
1438 	register wchar_t	*lp;
1439 
1440 	if (pattern) {
1441 		patlen = wcslen(pattern);
1442 		aux = (int *)malloc((size_t)patlen * sizeof (int));
1443 		if (aux == NULL)
1444 			return ((HISTPTR) NULL);
1445 		state = (int *)malloc((size_t)(patlen+1) * sizeof (int));
1446 		if (state == NULL) {
1447 			free(aux);
1448 			return ((HISTPTR) NULL);
1449 		}
1450 		if ((alt = patwcompile(pattern, patlen, aux)) != 0) {
1451 			for (hp = cur_line; hp; ) {
1452 				lp = hp->h_line;
1453 				if (patwmatch(pattern, aux,
1454 						lp, 0, wcslen(lp), alt, state))
1455 					break;
1456 				if (up)
1457 					hp = hp->h_prev;
1458 				else
1459 					hp = hp->h_next;
1460 			}
1461 		}
1462 		free((char *)aux);
1463 		free((char *)state);
1464 	}
1465 	if (!hp) {
1466 		if (!alt)
1467 			berror("%s", ebadpattern);
1468 		else
1469 			berror("%s", enotfound);
1470 		return ((HISTPTR) NULL);
1471 	}
1472 	return (hp);
1473 }
1474 
1475 
1476 /*
1477  * Implementation of basic editing functions.
1478  */
1479 LOCAL int
edit_line(cur_line)1480 edit_line(cur_line)
1481 	HISTPTR cur_line;
1482 {
1483 	register int		c;
1484 	register wchar_t	*lp, *cp;
1485 		wchar_t		*lpp;
1486 	unsigned	llen, maxlen;
1487 	int		diff;
1488 	int		multi = 0;
1489 
1490 	get_tty_modes(infile);
1491 #if	JOBCONTROL
1492 	tty_setpgrp(fdown(infile), mypgrp);
1493 #endif
1494 #ifdef	DEBUG
1495 	printf("editline at %p.\r\n", cur_line);
1496 #endif
1497 	maxlen = cur_line->h_len;
1498 	cp = lp = cur_line->h_line;
1499 	llen = wcslen(lp) + 1;
1500 	writews(lp);
1501 	cp = &lp[cur_line->h_pos];
1502 	backspace(linelen(cp));
1503 	ins_mode = !*cp;
1504 #ifdef	notneeded
1505 	eof = 0;
1506 #endif
1507 	for (;;) {
1508 		*cp ? set_insert_modes(infile) : set_append_modes(infile);
1509 		c = _nextwc();
1510 		switch (c) {
1511 
1512 		case '\r':		/* eigentlich nicht noetig */
1513 		case '\n':
1514 			delim = '\n';
1515 			putch('\r');
1516 			putch('\n');
1517 			bflush();
1518 			cur_line->h_len = maxlen;
1519 			cur_line->h_line = lp;
1520 			cur_line->h_pos = cp - lp;
1521 			reset_tty_modes();
1522 #ifdef	DEBUG
1523 			printf("ctlc: %d\r\n", ctlc);
1524 #endif
1525 			return ('\n');
1526 		case RETYPE:
1527 			redisp(lp, cp);
1528 			break;
1529 		case UNDO:
1530 			cp = undo_del(lp, cp, &llen);
1531 			break;
1532 		case SHOW:
1533 			show_files(lp, cp);
1534 			break;
1535 		case '\t':
1536 			if (multi) {
1537 				show_files(lp, cp);
1538 				break;
1539 			}
1540 			lpp = lp;
1541 			cp = exp_files(&lpp, cp, &llen, &maxlen, &multi);
1542 			if (lpp == NULL)
1543 				return ('\0');
1544 			lp = lpp;
1545 			break;
1546 		case BACKSPACE:
1547 			multi = 0;
1548 			if (cp > lp)
1549 				backspace(chlen(*(--cp)));
1550 			else
1551 				beep();
1552 			break;
1553 		case FORWARD:
1554 			multi = 0;
1555 			if (*cp) {
1556 				writec(*cp++);
1557 				bflush();
1558 			}
1559 			else
1560 				beep();
1561 			break;
1562 		case EOF:
1563 #ifdef	notneeded
1564 			if (++eof < 4)
1565 				break;
1566 			clearerr(infile);
1567 #endif
1568 			if (delim == EOF)
1569 				exitbsh(0);
1570 			/* FALLTHROUGH */
1571 		case CTRLD:
1572 			multi = 0;
1573 			if ((cp == lp && !wcslen(lp) &&
1574 				!ev_eql(ignoreeofname, on) &&
1575 				!(__ign_eof && (*__ign_eof)())) || c == EOF) {
1576 				delim = EOF;
1577 				clearline(lp, cp);
1578 				reset_tty_modes();
1579 				putch('\r');
1580 				putch('\n');
1581 				writes("<EOF>");
1582 				putch('\r');
1583 				putch('\n');
1584 				bflush();
1585 				return (EOF);
1586 			}
1587 			if (*cp) {
1588 				writec(*cp);
1589 				bflush();
1590 				del_char(cp);
1591 				--llen;
1592 			}
1593 			else
1594 				beep();
1595 
1596 			break;
1597 		case CTRLC:
1598 			multi = 0;
1599 			delim = CTRLC;
1600 #ifndef	LIB_SHEDIT
1601 			ctlc++;			/* Mark for bsh */
1602 #endif
1603 			*lp = 0;
1604 			llen = 1;		/* NULL Byte !!! */
1605 			cp = lp;
1606 			writes("^C");
1607 			return (EOF);
1608 		case CTRLU:
1609 			multi = 0;
1610 			clearline(lp, cp);
1611 			*lp = 0;
1612 			llen = 1;		/* NULL Byte !!! */
1613 			cp = lp;
1614 			break;
1615 		case CTRLW:
1616 			multi = 0;
1617 			cp = clearword(lp, cp, &llen);
1618 			break;
1619 		case ESC:
1620 			multi = 0;
1621 			c = get_request();
1622 			if (c == RESTORE)
1623 				clearline(lp, cp);
1624 			if (c & ~BYTEMASK) {
1625 				cur_line->h_line = lp;
1626 				cur_line->h_len = maxlen;
1627 				cur_line->h_pos = cp - lp;
1628 				reset_tty_modes();
1629 				return (c);
1630 			}
1631 			else
1632 				cp = esc_process(c, lp, cp, &llen);
1633 			break;
1634 		case UPWARD:
1635 		case DOWNWARD:
1636 			clearline(lp, cp);
1637 			cur_line->h_line = lp;
1638 			cur_line->h_len = maxlen;
1639 			cur_line->h_pos = cp - lp;
1640 			return (c);
1641 		case DELETE:
1642 			multi = 0;
1643 			if (cp > lp) {
1644 				del_char(--cp);
1645 				llen--;
1646 			}
1647 			else
1648 				beep();
1649 			break;
1650 		case BEGINLINE:
1651 			multi = 0;
1652 			backspace(linediff(lp, cp));
1653 			cp = lp;
1654 			break;
1655 		case ENDLINE:
1656 			multi = 0;
1657 			while (*cp)
1658 				writec(*cp++);
1659 			bflush();
1660 			break;
1661 		case QUOTECH:
1662 		case CTRLV: {	int	oc = c;
1663 
1664 			set_insert_modes(infile);
1665 			c = _nextwc();
1666 
1667 			if (oc == QUOTECH &&
1668 			    (towupper(c) < 0140) && c >= '@')
1669 				c &= 037;
1670 			}
1671 			/* FALLTHROUGH */
1672 		default:
1673 			multi = 0;
1674 			if (i_should_echo || !iswprint(c))
1675 				writec(c);
1676 			bflush();
1677 			if (llen == maxlen) {
1678 				maxlen += LINEQUANT;
1679 				diff = cp - lp;
1680 				lp = new_line(lp, llen, maxlen);
1681 				if (lp == NULL)
1682 					return ('\0');
1683 				cp = lp + diff;
1684 			}
1685 			ins_char(cp, c);
1686 			cp++;
1687 			llen++;
1688 		}
1689 	}
1690 }
1691 
1692 
1693 /*
1694  * Redisplay current line.
1695  */
1696 LOCAL void
redisp(lp,cp)1697 redisp(lp, cp)
1698 	wchar_t	*lp;
1699 	wchar_t	*cp;
1700 {
1701 	(void) fprintf(stderr, "\r\n%s", iprompt);
1702 	(void) fflush(stderr);
1703 	writews(lp);
1704 	backspace(linelen(cp));
1705 }
1706 
1707 
1708 /*
1709  * Insert a string a current cursor position.
1710  */
1711 LOCAL wchar_t *
insert(cp,s,lenp)1712 insert(cp, s, lenp)
1713 	register wchar_t	*cp;
1714 	register wchar_t	*s;
1715 	unsigned		*lenp;
1716 {
1717 	*lenp += wcslen(s);
1718 	writews(s);
1719 	cp = ins_word(cp, s);
1720 	return (cp);
1721 }
1722 
1723 
1724 /*
1725  * Undo last delete(s).
1726  */
1727 LOCAL wchar_t *
undo_del(lp,cp,lenp)1728 undo_del(lp, cp, lenp)
1729 	register wchar_t	*lp;
1730 	register wchar_t	*cp;
1731 	unsigned		*lenp;
1732 {
1733 #ifdef	NEW
1734 	register int	fdellen;
1735 
1736 	cp = insert(cp, "undo", lenp);
1737 	cp = insert(cp, "del", lenp);
1738 	fdellen = strlen("del");
1739 	backspace(fdellen);
1740 	cp -= fdellen;
1741 #endif
1742 	return (cp);
1743 }
1744 
1745 /*
1746  * Check whether a wide char code can be found in a narrow char string.
1747  */
1748 LOCAL char *
strwchr(s,c)1749 strwchr(s, c)
1750 	char	*s;
1751 	wchar_t	c;
1752 {
1753 	while (*s) {
1754 		if ((*s++ & 0xFF) == c)
1755 			return (--s);
1756 	}
1757 	return (NULL);
1758 }
1759 
1760 /*
1761  * The characters ' ' ... '&' are handled by the shell parser,
1762  * the characters '!' ... '$' are pattern matcher meta characters.
1763  * The complete pattern matcher meta characters are "!#%*?\\{}[]^$", but
1764  * the '%' has been checked before in the shell parser charracter set.
1765  * The string quoting charcters '"' and '\''.
1766  */
1767 LOCAL char xchars[] = " \t\"'<>%|;()&-!#*?\\{}[]^$"; /* Chars that need quoting */
1768 
1769 /*
1770  * Expand a wide char string and return a malloc()ed copy.
1771  * Any character that needs quoting is prepended with a '\\'.
1772  */
1773 LOCAL wchar_t *
xpwcs(cp,qoff)1774 xpwcs(cp, qoff)
1775 	wchar_t	*cp;
1776 	int	qoff;
1777 {
1778 	wchar_t	*ret;
1779 	wchar_t	*p = &cp[qoff];
1780 	int	len = qoff;
1781 
1782 	/*
1783 	 * Count characters past "qoff" twice if they need quoting.
1784 	 */
1785 	while (*p) {
1786 		len++;
1787 		if (strwchr(xchars, *p++))
1788 			len++;
1789 	}
1790 	ret = malloc((len+1) * sizeof (wchar_t));
1791 	if (ret == NULL)
1792 		return (ret);
1793 	/*
1794 	 * First copy over the pre quoting offset characters literarily.
1795 	 */
1796 	for (p = ret; --qoff >= 0; ) {
1797 		*p++ = *cp++;
1798 	}
1799 	/*
1800 	 * Now qoute all needed characters from the input string.
1801 	 */
1802 	while (*cp) {
1803 		if (strwchr(xchars, *cp))
1804 			*p++ = '\\';
1805 		*p++ = *cp++;
1806 	}
1807 	*p = '\0';
1808 	return (ret);
1809 }
1810 
1811 /*
1812  * Expand tilde.
1813  * delp returns the number of characters to delete left to the cursor.
1814  * Returns the allocated replacement as wide string.
1815  */
1816 LOCAL wchar_t *
xp_tilde(ns,delp)1817 xp_tilde(ns, delp)
1818 	char	*ns;
1819 	int	*delp;
1820 {
1821 	struct passwd	*pw;
1822 	char		*ep = NULL;	/* Keep GCC happy ;-) */
1823 	int		dels = 0;
1824 
1825 	if (*ns == '\0') {
1826 		dels = 1;
1827 		ep = getcurenv("HOME");
1828 	} else if (*ns == '+' && ns[1] == '\0') {
1829 		dels = 2;
1830 		ep = getcurenv("PWD");
1831 	} else if (*ns == '-' && ns[1] == '\0') {
1832 		dels = 2;
1833 		ep = getcurenv("OLDPWD");
1834 	}
1835 	if (dels) {
1836 		if (ep == NULL)
1837 			return (0);
1838 		if (delp)
1839 			*delp = dels;
1840 		return (towcs((wchar_t *)NULL, 0, ep, -1));
1841 	}
1842 	dels = strlen(ns) + 1;
1843 
1844 	pw = getpwnam(ns);
1845 	endpwent();
1846 	if (pw == NULL)
1847 		return (0);
1848 	if (delp)
1849 		*delp = dels;
1850 	return (towcs((wchar_t *)NULL, 0, pw->pw_dir, -1));
1851 }
1852 
1853 /*
1854  * The characters ' ' ... '&' are handled by the shell parser as word separators
1855  */
1856 LOCAL char wschars[] = " \t<>%|;()&"; /* Chars that are word separators */
1857 
1858 /*
1859  * Expand filenames (implement file name completion).
1860  * This is the basic function that either expands or lists filenames.
1861  * It gets called by exp_files() and by show_files().
1862  *
1863  * Returns pointer with string to insert
1864  */
1865 LOCAL wchar_t *
xp_files(lp,cp,show,multip,delp)1866 xp_files(lp, cp, show, multip, delp)
1867 	register wchar_t	*lp;	/* Begin of current line	*/
1868 	register wchar_t	*cp;	/* Current cursor position	*/
1869 		BOOL	show;	/* Show list of multi-results		*/
1870 		int	*multip; /* Found mult results in non show mode */
1871 		int	*delp;	/* Chars to delete before cursor	*/
1872 {
1873 	Tnode	*np;
1874 	Tnode	*l1;
1875 	wchar_t	*wp;
1876 	wchar_t	*wp2 = NULL;
1877 	wchar_t	*tp;
1878 	int	len;
1879 	int	xlen;
1880 	int	dir = 0;
1881 	wchar_t	*p1;
1882 	wchar_t	*p2 = NULL;
1883 	char	*ns;
1884 	int	multi = 0;	/* No mutiple results found yet	*/
1885 
1886 	/*
1887 	 * Check whether to expand (complete) the filename.
1888 	 * This depends on the character that is currently under the cursor.
1889 	 */
1890 	if (*cp != '\0' && !strwchr(wschars, *cp)) {
1891 #ifdef	__do_beep_when_in_word__
1892 		beep();
1893 #endif
1894 		return (0);
1895 	}
1896 	if (show) {
1897 		(void) fprintf(stderr, "\r\n");
1898 		(void) fflush(stderr);
1899 	}
1900 	/*
1901 	 * Set "wp" to current cursor position and then step back into the text
1902 	 */
1903 	wp = cp;
1904 	if (*wp == '\0' || strwchr(wschars, *wp))
1905 		wp--;
1906 
1907 
1908 	/*
1909 	 * Step back to the beginning of the current word.
1910 	 * Do not stop on quoted delimiters.
1911 	 */
1912 again:
1913 	while (wp > lp && !strwchr(wschars, *wp))
1914 		wp--;
1915 	if (wp > lp && strwchr(wschars, *wp) &&
1916 	    wp[-1] == '\\') {
1917 		wp--;
1918 		goto again;
1919 	}
1920 
1921 
1922 	/*
1923 	 * Advance again into current word.
1924 	 */
1925 	if (strwchr(wschars, *wp))
1926 		wp++;
1927 
1928 
1929 	len = cp - wp;
1930 	tp = malloc((len+2) * sizeof (wchar_t));
1931 	if (tp == NULL)
1932 		return (0);
1933 	wcsncpy(tp, wp, len);
1934 	tp[len] = '\0';
1935 	if (tp[0] != '~') {
1936 		for (wp = tp; *wp; wp++) {
1937 			if (wp[0] == '\\' && wp[1] != '\0') {
1938 				wchar_t	*w1 = wp++;
1939 				wchar_t	*w2 = wp;
1940 				while (*w2)
1941 					*w1++ = *w2++;
1942 				*w1 = '\0';
1943 				len--;
1944 			}
1945 		}
1946 	}
1947 	ns = tombs(NULL, 0, tp, -1);
1948 	free(tp);
1949 	if (ns == NULL)
1950 		return (0);
1951 
1952 	if (ns[0] == '~')
1953 		return (xp_tilde(++ns, delp));
1954 
1955 	/*
1956 	 * Call path name expand routing from bsh
1957 	 *
1958 	 * XXX: If the characters to the left of the cursor contain pattern meta
1959 	 * XXX: characters that are not escaped, this will cause false matches.
1960 	 */
1961 	np = bexpand(ns);
1962 	free(ns);
1963 	if (np == NULL) {
1964 #ifdef	__do_beep_when_expand_to_null__
1965 		beep();
1966 #endif
1967 		return (0);
1968 	}
1969 
1970 	/*
1971 	 * More than one result?
1972 	 */
1973 	if (np->tn_right.tn_node)
1974 		multi++;
1975 					/* wp = first from list	 */
1976 	wp = towcs((wchar_t *)NULL, 0, np->tn_left.tn_str, -1);
1977 	if (wp == NULL)
1978 		goto out;
1979 	p2 = wp;
1980 	wp = xpwcs(wp, len);		/* Insert '\\' if needed */
1981 	free(p2);
1982 	p2 = NULL;
1983 	if (wp == NULL)
1984 		goto out;
1985 	p2 = wcsrchr(wp, '/');		/* Find last slash		    */
1986 	xlen = 0;			/* If no slash start from str begin */
1987 	if (p2)				/* If we found a slash		    */
1988 		xlen = p2 - wp + 1;	/* start right to slash		    */
1989 	p2 = NULL;
1990 	for (l1 = np; l1 != (Tnode *)NULL; l1 = l1->tn_right.tn_node) {
1991 		if (l1->tn_right.tn_node == NULL) {
1992 			/*
1993 			 * Last in list for max. differences to
1994 			 * compare with first one.
1995 			 */
1996 			p2 = towcs((wchar_t *)NULL, 0, l1->tn_left.tn_str, -1);
1997 		}
1998 		if (show) {
1999 			wchar_t	*p;
2000 			p = towcs((wchar_t *)NULL, 0, l1->tn_left.tn_str, -1);
2001 			if (p) {
2002 				writews(&p[xlen]);
2003 				writes(" ");
2004 				free(p);
2005 			}
2006 		}
2007 	}
2008 	/*
2009 	 * For multiple results, find longest common match.
2010 	 */
2011 	if (multi) {
2012 		if (p2 == NULL)	/* towcs() in for() loop returned NULL */
2013 			goto out;
2014 		wp2 = xpwcs(p2, len);
2015 		free(p2);
2016 		p2 = NULL;
2017 		if (wp2 == NULL)
2018 			goto out;
2019 		for (p1 = wp, p2 = wp2; *p1; ) {
2020 			if (*p1++ != *p2++) {
2021 				p1--;
2022 				break;
2023 			}
2024 		}
2025 		xlen = p1 - wp;
2026 		if (*p1 == '\0' && *p2 == '\0') {
2027 			p1 = NULL;
2028 		}
2029 	} else {
2030 		xlen = wcslen(wp);
2031 		p1 = NULL;
2032 		if (p2)
2033 			free(p2);
2034 	}
2035 	p2 = NULL;
2036 
2037 	if (!show) {
2038 		if (p1 == NULL)
2039 			dir = is_dir(np->tn_left.tn_str);
2040 		if ((xlen - len) > 0 || dir) {
2041 			p2 = malloc((xlen-len+2) * sizeof (wchar_t));
2042 		} else {
2043 			/*
2044 			 * Nothing to add.
2045 			 */
2046 			p2 = NULL;
2047 #ifdef	__do_beep_when_nothing_to_add__
2048 			beep();
2049 #endif
2050 			if (multip)
2051 				*multip = multi;
2052 		}
2053 	}
2054 	/*
2055 	 * xlen-len may be negative in case that there was a false match
2056 	 * that results in directory entries that do not start with
2057 	 * the last word in the line. This happens when there are unescaped
2058 	 * pattern meta characters in the last word on the line.
2059 	 */
2060 	if (!show && p2 && (xlen-len) >= 0) {
2061 		wcsncpy(p2, &wp[len], xlen-len);
2062 		if (p1) {
2063 			if ((xlen - len) == 0)
2064 				beep();
2065 			p2[xlen-len] = '\0';
2066 		} else {
2067 			p2[xlen-len] = dir ? '/' : ' ';
2068 			p2[xlen-len+1] = '\0';
2069 		}
2070 	} else {
2071 		p2 = NULL;
2072 	}
2073 out:
2074 	freetree(np);
2075 	if (wp)
2076 		free(wp);
2077 	if (wp2)
2078 		free(wp2);
2079 	return (p2);
2080 }
2081 
2082 
2083 /*
2084  * Expand filenames (implement file name completion).
2085  * Insert expansion result into current line if applicable.
2086  */
2087 LOCAL wchar_t *
exp_files(lpp,cp,lenp,maxlenp,multip)2088 exp_files(lpp, cp, lenp, maxlenp, multip)
2089 	register wchar_t	**lpp;
2090 	register wchar_t	*cp;
2091 	unsigned		*lenp;
2092 	unsigned		*maxlenp;
2093 		int		*multip;
2094 {
2095 	wchar_t	*p;
2096 	int	diff;
2097 	int	del = 0;
2098 
2099 	p = xp_files(*lpp, cp, FALSE, multip, &del);
2100 	if (p) {
2101 		diff = wcslen(p);
2102 		if (*lenp + diff >= *maxlenp) {
2103 			diff = ((diff + LINEQUANT)/LINEQUANT) * LINEQUANT;
2104 			*maxlenp += diff;
2105 			diff = cp - *lpp;
2106 			*lpp = new_line(*lpp, *lenp, *maxlenp);
2107 			if (*lpp == NULL) {
2108 				free(p);
2109 				return ((wchar_t *)NULL);
2110 			}
2111 			cp = *lpp + diff;
2112 		}
2113 		while (--del >= 0 && cp > *lpp) {
2114 			del_char(--cp);
2115 			(*lenp)--;
2116 		}
2117 		cp = insert(cp, p, lenp);
2118 		free(p);
2119 	} else {
2120 		beep();
2121 	}
2122 	return (cp);
2123 }
2124 
2125 
2126 /*
2127  * Show filename list (implement file name completion).
2128  */
2129 LOCAL void
show_files(lp,cp)2130 show_files(lp, cp)
2131 	register wchar_t	*lp;
2132 	register wchar_t	*cp;
2133 {
2134 		wchar_t		*p;
2135 
2136 	p = xp_files(lp, cp, TRUE, NULL, NULL);
2137 	if (p)
2138 		free(p);
2139 	redisp(lp, cp);
2140 }
2141 
2142 
2143 /*
2144  * Get an ESC request
2145  */
2146 LOCAL int
get_request()2147 get_request()
2148 {
2149 	register int c;
2150 
2151 		set_insert_modes(infile);
2152 	c = _nextwc();
2153 	if (c == UPWARD || c == DOWNWARD)
2154 		return (c | SEARCH);
2155 	if (c == '\n' || c == '\r')
2156 		return (RESTORE);
2157 	return (c);
2158 }
2159 
2160 
2161 /*
2162  * Implementation of functions that follow an ESC in an ESC sequence.
2163  */
2164 LOCAL wchar_t *
esc_process(xc,lp,cp,lenp)2165 esc_process(xc, lp, cp, lenp)
2166 	register int		xc;
2167 	register wchar_t		*lp;
2168 	register wchar_t	*cp;
2169 		unsigned	*lenp;
2170 {
2171 	register wchar_t	*pp;
2172 	register int	dl = 0;
2173 
2174 	switch (xc) {
2175 
2176 	case BACKSPACE:
2177 		if (cp == lp) {
2178 			beep();
2179 			return (cp);
2180 		}
2181 		cp--;
2182 		while (cp >= lp && *cp == BLANK) {
2183 			--cp, dl++;
2184 		}
2185 		while (cp >= lp && *cp != BLANK)
2186 			dl += chlen(*cp--);
2187 		backspace(dl);
2188 		return (++cp);
2189 	case FORWARD:
2190 		if (!*cp)
2191 			beep();
2192 		else {
2193 			while (*cp && *cp != BLANK)
2194 				writec(*cp++);
2195 			while (*cp == BLANK)
2196 				writec(*cp++);
2197 			bflush();
2198 		}
2199 		return (cp);
2200 	case DELETE:
2201 	case CTRLD:
2202 		pp = cp;
2203 		if (xc == CTRLD) {
2204 			if (!*cp) {
2205 				beep();
2206 				return (cp);
2207 			}
2208 			while (*pp && *pp != BLANK)
2209 				dl += chlen(*pp++);
2210 			while (*pp == BLANK)
2211 				dl += chlen(*pp++);
2212 		} else {
2213 			if (cp == lp) {
2214 				beep();
2215 				return (cp);
2216 			}
2217 			while (--cp > lp && *cp == BLANK);
2218 			while (cp > lp && *(cp-1) != BLANK)
2219 				--cp;
2220 			dl = linediff(cp, pp);
2221 			backspace(dl);
2222 		}
2223 		/*
2224 		 * strcpy() doesn't handle overlapped buffers
2225 		 */
2226 		{ wchar_t *p2 = cp; wchar_t *p1 = pp;
2227 			while ((*p2++ = *p1++) != '\0')
2228 				;
2229 		}
2230 		writews(cp);
2231 		space(dl);
2232 		backspace(linelen(cp) + dl);
2233 		(*lenp) -= (pp - cp);
2234 		return (cp);
2235 	default:
2236 		beep();
2237 		return (cp);
2238 	}
2239 }
2240 
2241 
2242 /*
2243  * Interactive line editor function.
2244  * Allows only a limited subset if the editing functions.
2245  */
2246 LOCAL wchar_t *
sget_line()2247 sget_line()
2248 {
2249 		wchar_t		*lp;
2250 		BOOL		edit = TRUE;
2251 		int		cmd;
2252 	register HISTPTR	tmp_line;
2253 
2254 	tmp_line = mktmp();
2255 	if (tmp_line == NULL)
2256 		return (NULL);
2257 	while (edit) {
2258 		cmd = edit_line(tmp_line);
2259 		switch (cmd) {
2260 
2261 		case UPWARD:
2262 		case DOWNWARD:
2263 		case SEARCHUP:
2264 		case SEARCHDOWN:
2265 		case RESTORE:
2266 				beep();
2267 				break;
2268 		case '\0':
2269 		case EOF:
2270 				return (NULL);
2271 		case '\r':
2272 		case '\n':
2273 				edit = FALSE;
2274 		}
2275 	}
2276 	lp = tmp_line->h_line;
2277 	append_hline(lp, tmp_line->h_len);
2278 	free((char *)tmp_line);
2279 	return (lp);
2280 }
2281 
2282 
2283 /*
2284  * Interactive line editor function.
2285  * Allows all editing functions.
2286  */
2287 LOCAL wchar_t *
iget_line()2288 iget_line()
2289 {
2290 	register wchar_t	*lp;
2291 		wchar_t		*np;
2292 		BOOL		edit = TRUE;
2293 	register int		cmd = '\0';
2294 	register HISTPTR	cur_line;
2295 	register HISTPTR	tmp_line;			/* empty tmp */
2296 	register HISTPTR	save_line;
2297 	register HISTPTR	etmp_line = (HISTPTR) NULL;	/* tmp copy */
2298 	register HISTPTR	orig_line = (HISTPTR) NULL;	/* tmp orig */
2299 
2300 	save_line = cur_line = tmp_line = mktmp();
2301 	if (cur_line == NULL)
2302 		return (NULL);
2303 
2304 	lp = cur_line->h_line;
2305 	cur_line->h_prev = last_line;
2306 	cur_line->h_next = first_line;
2307 #ifdef DEBUG
2308 	printf("History: first at %p, last at %p\r\n", first_line, last_line);
2309 #endif
2310 	while (edit) {
2311 		if (!(cmd & ~BYTEMASK))
2312 			save_line = cur_line;
2313 		cmd = edit_line(cur_line);
2314 #ifdef DEBUG
2315 		printf("Edited line at %p, line at %p '%s'.\r\n", cur_line,
2316 					cur_line->h_line, cur_line->h_line);
2317 #endif
2318 		switch (cmd) {
2319 
2320 		case UPWARD:
2321 		case DOWNWARD:
2322 		case SEARCHUP:
2323 		case SEARCHDOWN:
2324 #ifdef DEBUG
2325 			printf("Changing line at %p.\r\n", cur_line);
2326 #endif
2327 			if ((cmd & BYTEMASK) == UPWARD)
2328 				cur_line = cur_line->h_prev;
2329 			else
2330 				cur_line = cur_line->h_next;
2331 			if (etmp_line) {
2332 				if (etmp_line == save_line)
2333 					save_line = orig_line;
2334 				unhold_line(etmp_line, orig_line);
2335 				etmp_line = NULL;
2336 			}
2337 #ifdef DEBUG
2338 			printf("New line at %p.\r\n", cur_line);
2339 #endif
2340 			if (cur_line == (HISTPTR) NULL)
2341 				cur_line = tmp_line;
2342 			if ((cmd & ~BYTEMASK) == SEARCH) {
2343 				cur_line = match_input(cur_line,
2344 						tmp_line, cmd == SEARCHUP);
2345 			}
2346 			orig_line = cur_line;
2347 			etmp_line = cur_line = hold_line(cur_line);
2348 			if (etmp_line == NULL)
2349 				cur_line = orig_line;
2350 			break;
2351 		case RESTORE:
2352 			if (etmp_line) {
2353 				if (etmp_line == save_line)
2354 					save_line = orig_line;
2355 				unhold_line(etmp_line, orig_line);
2356 				etmp_line = NULL;
2357 			}
2358 			cur_line = save_line;
2359 			break;
2360 		case '\0':
2361 		case EOF:
2362 			return (NULL);
2363 		case '\r':
2364 		case '\n':
2365 #ifdef DEBUG
2366 			printf("End of line.\r\n");
2367 #endif
2368 			edit = FALSE;
2369 		}
2370 	}
2371 	lp = cur_line->h_line;
2372 	np = makewstr(lp);
2373 	if (cur_line == tmp_line || cur_line == etmp_line) {
2374 #ifdef DEBUG
2375 		printf("tmp_line to append....\r\n");
2376 #endif
2377 		if (*lp)
2378 			append_wline(lp, cur_line->h_len, cur_line->h_pos);
2379 	} else {
2380 #ifdef DEBUG
2381 		printf("previous line to move....\r\n");
2382 #endif
2383 		if (*lp)
2384 			move_to_end(cur_line);
2385 	}
2386 
2387 	stripout();
2388 	if ((tmp_line->h_flags & F_TMP) != 0) {
2389 		free_line(tmp_line);
2390 	}
2391 	if (etmp_line && (etmp_line->h_flags & F_TMP) != 0) {
2392 		free_line(etmp_line);
2393 	}
2394 #ifdef DEBUG
2395 	printf("History: first at %p, last at %p\r\n", first_line, last_line);
2396 #endif
2397 	return (np);
2398 }
2399 
2400 
2401 /*
2402  * Read a line using func pointer and return result in allocated string.
2403  */
2404 /* VARARGS1 */
2405 EXPORT char *
2406 make_line(f, arg)
2407 	register int	(*f) __PR((MYFILE *));
2408 	register MYFILE	*arg;
2409 {
2410 	register unsigned	maxl;
2411 	register unsigned	llen;
2412 			char	*lp;
2413 	register	char	*p;
2414 	register 	int	c;
2415 
2416 #ifdef DEBUG
2417 	printf("        make_line\r\n");
2418 #endif
2419 	lp = p = malloc((maxl = LINEQUANT) * sizeof (*lp));
2420 	if (lp == NULL)
2421 		return (lp);
2422 	llen = 0;
2423 	for (;;) {
2424 		if ((c = (*f)(arg)) == EOF || c == '\n' || c == '\205') {
2425 			if (c == EOF)
2426 				delim = EOF;
2427 			else
2428 				delim = '\n'; /* XXX ??? \205 ??? */
2429 			*p = 0;
2430 			return (lp);
2431 		}
2432 		*p++ = (char)c;
2433 		if (++llen == maxl) {
2434 			maxl += LINEQUANT;
2435 			lp = new_line(lp, llen, maxl);
2436 			if (lp == NULL)
2437 				return (lp);
2438 			p = lp + llen;
2439 		}
2440 	}
2441 }
2442 
2443 
2444 /*
2445  * Read a line from a file in a way compatible to [is]get_line().
2446  */
2447 LOCAL char *
fread_line(f)2448 fread_line(f)
2449 	MYFILE	*f;
2450 {
2451 	extern int	fgetc __PR((MYFILE *));
2452 
2453 	return (make_line(fgetc, f));
2454 }
2455 
2456 
2457 /*
2458  * External interface to the line editor.
2459  * Use fread_line() if input is from a file.
2460  * Use iget_line() if input is from tty and promt counter is 0.
2461  * Use sget_line() if input is from tty and promt counter is > 0.
2462  */
2463 EXPORT char *
get_line(n,f)2464 get_line(n, f)
2465 	int	n;		/* Prompt index */
2466 	MYFILE	*f;		/* FILE * to read from */
2467 {
2468 	if (line_pointer) {
2469 		free(line_pointer);
2470 		line_pointer = NULL;
2471 	}
2472 	if (wline_pointer) {
2473 		free(wline_pointer);
2474 		wline_pointer = NULL;
2475 	}
2476 	if (ttyflg && mp_init) {
2477 		map_init();
2478 		term_init();
2479 	}
2480 	if (prflg) {
2481 		iprompt = prompts[n?1:0];
2482 		(void) fprintf(stderr, "%s", iprompt);
2483 		(void) fflush(stderr);
2484 	}
2485 	if (!ttyflg) {
2486 #ifdef	DEBUG
2487 		printf("        get_line: fread_line(%p).\r\n", f);
2488 #endif
2489 		line_pointer = fread_line(f);
2490 		if (line_pointer == NULL)
2491 			return (nullstr);
2492 		return (line_pointer);
2493 	} else {
2494 		infile = f;
2495 		tty_init();
2496 		if (n)
2497 			wline_pointer = sget_line();	/* Editor w/o history */
2498 		else
2499 			wline_pointer = iget_line();	/* Editor w. history */
2500 		tty_term();
2501 	}
2502 
2503 	if (wline_pointer == NULL)
2504 		return (nullstr);
2505 
2506 	line_pointer = tombs(NULL, 0, wline_pointer, -1);
2507 	return (line_pointer?line_pointer:nullstr);
2508 }
2509 
2510 /*
2511  * Write current content of the history to an open file.
2512  * If we like to be compatible to what was used by "#v on" on UNOS,
2513  * add {} brackets if we are writing to stdout.
2514  */
2515 EXPORT int
put_history(f,flg,first,last,subst)2516 put_history(f, flg, first, last, subst)
2517 	register MYFILE	*f;
2518 	register int	flg;
2519 		int	first;
2520 		int	last;
2521 		char	*subst;
2522 {
2523 	register HISTPTR p;
2524 	register HISTPTR pe = last_line;
2525 		size_t	oldlen = 0;	/* make silly GCC happy */
2526 		char	*eqp = NULL;	/* make silly GCC happy */
2527 
2528 	if (f == NULL)
2529 		f = gstd[1];
2530 
2531 	if (subst) {
2532 		eqp = strchr(subst, '=');
2533 		oldlen = eqp++ - subst;
2534 	}
2535 
2536 	if (first < 0) {
2537 		register int	i;
2538 
2539 		for (i = 0, p = last_line; p; p = p->h_prev) {
2540 			if (--i == first)
2541 				break;
2542 		}
2543 		if (p == NULL)
2544 			p = first_line;
2545 	} else if (first > 0) {
2546 		for (p = last_line; p; p = p->h_prev) {
2547 			if (p->h_number == first)
2548 				break;
2549 		}
2550 		if (p == NULL)
2551 			return (-1);
2552 	} else {
2553 		p = first_line;
2554 	}
2555 	if (last) {
2556 		for (pe = last_line; pe; pe = pe->h_prev) {
2557 			if (pe->h_number == last)
2558 				break;
2559 		}
2560 		if (pe == NULL)
2561 			pe = last_line;
2562 	}
2563 	if (flg & HI_REVERSE) {
2564 		HISTPTR pt;
2565 		pt = p;
2566 		p = pe;
2567 		pe = pt;
2568 	}
2569 	for (; p; p = (flg & HI_REVERSE) ? p->h_prev : p->h_next) {
2570 		if (ctlc && (flg & HI_INTR))
2571 			break;
2572 		if (f == stdout) {	/* Used to print history */
2573 			register wchar_t	*cp;
2574 			register wchar_t	wc;
2575 				char		s[16];
2576 #ifdef	JOS_VERBOSE			/* Make output similar to JOS #v on */
2577 			_writes("{ ");
2578 #endif
2579 
2580 			if ((flg & HI_NONUM) == 0) {
2581 				js_snprintf(s, sizeof (s), "%d", p->h_number);
2582 				_writes(s);
2583 			}
2584 			if (flg & HI_TAB)
2585 				putch('\t');
2586 			for (cp = p->h_line; (wc = *cp++) != '\0'; ) {
2587 				(flg & HI_PRETTYP) ?
2588 					prettyp(wc, &buf) :
2589 					putch(wc);
2590 				if (wc == '\n')
2591 					putch('\t');
2592 			}
2593 
2594 #ifdef	JOS_VERBOSE			/* Make output similar to JOS #v on */
2595 			_writes(" }");
2596 #endif
2597 			putch('\n');
2598 		} else {		/* This is the save_history() variant */
2599 			char	line[512];
2600 			char	*lp;
2601 			char	*lp2;
2602 #ifndef	USE_ANSI_NL_SEPARATOR
2603 			char	*cp;
2604 #endif
2605 
2606 			lp2 = lp = tombs(line, sizeof (line), p->h_line, -1);
2607 			if (lp == NULL)
2608 				continue;
2609 
2610 			if (subst && strncmp(lp, subst, oldlen) == 0) {
2611 				lp2 += oldlen;
2612 				fprintf(f, "%s", eqp);
2613 			}
2614 #ifndef	USE_ANSI_NL_SEPARATOR
2615 			if (flg & HI_ANSI_NL) {
2616 				for (cp = lp2; *cp; cp++) {
2617 					if (*cp == '\n')
2618 						*cp = '\205';
2619 				}
2620 			}
2621 #endif
2622 			/*
2623 			 * XXX could be fprintf(f, "%ls\n", p->h_line);
2624 			 */
2625 			fprintf(f, "%s\n", lp2);
2626 			if (lp != line)
2627 				free(lp);
2628 		}
2629 		if (p == pe)
2630 			break;
2631 	}
2632 	if (f == stdout)
2633 		bflush();
2634 
2635 	return (0);
2636 }
2637 
2638 EXPORT HISTPTR
_search_history(flg,first,pat)2639 _search_history(flg, first, pat)
2640 	register int	flg;
2641 		int	first;
2642 		char	*pat;
2643 {
2644 	register HISTPTR p;
2645 	wchar_t		wline[512];
2646 	wchar_t		*wp = NULL;
2647 	size_t		wlen = 0;	/* make silly GCC happy */
2648 
2649 	if (pat) {
2650 		wp = towcs(wline, sizeof (wline) / sizeof (wline[0]), pat, -1);
2651 		if (wp == NULL)
2652 			return ((HISTPTR)NULL);
2653 		wlen = wcslen(wp);
2654 	}
2655 	if (first < 0) {
2656 		register int	i;
2657 
2658 		for (i = 0, p = last_line; p; p = p->h_prev) {
2659 			if (--i == first)
2660 				break;
2661 		}
2662 		if (p == NULL)
2663 			p = first_line;
2664 	} else if (first > 0) {
2665 		for (p = last_line; p; p = p->h_prev) {
2666 			if (p->h_number == first)
2667 				break;
2668 		}
2669 		if (p == NULL)
2670 			return (p);
2671 	} else {
2672 		p = first_line;
2673 	}
2674 	if (pat) {
2675 		for (; p; p = p->h_next) {
2676 			if (ctlc && (flg & HI_INTR)) {
2677 				p = NULL;
2678 				break;
2679 			}
2680 			if (wcsncmp(wp, p->h_line, wlen) == 0)
2681 				break;
2682 		}
2683 		if (wp != wline)
2684 			free(wp);
2685 	}
2686 	return (p);
2687 }
2688 
2689 EXPORT int
search_history(flg,first,pat)2690 search_history(flg, first, pat)
2691 	register int	flg;
2692 		int	first;
2693 		char	*pat;
2694 {
2695 	register HISTPTR p = _search_history(flg, first, pat);
2696 
2697 	if (p == NULL)
2698 		return (-1);
2699 	return (p->h_number);
2700 }
2701 
2702 EXPORT int
remove_history(flg,first,pat)2703 remove_history(flg, first, pat)
2704 	register int	flg;
2705 		int	first;
2706 		char	*pat;
2707 {
2708 	register HISTPTR p = _search_history(flg, first, pat);
2709 
2710 	if (p == NULL)
2711 		return (-1);
2712 
2713 	remove_line(p);
2714 	return (0);
2715 }
2716 
2717 #define	HF_READ	0
2718 #define	HF_SAVE	1
2719 
2720 LOCAL char *
get_histfname(flag)2721 get_histfname(flag)
2722 	int	flag;
2723 {
2724 	if (hfilename)
2725 		free(hfilename);
2726 	/*
2727 	 * First check for "HISTFILE" as this is required by POSIX.
2728 	 */
2729 	if ((hfilename = getcurenv(histfilename)) != NULL) {
2730 		hfilename = concat(hfilename, (char *)NULL);
2731 	} else {
2732 		/*
2733 		 * Our historic name is "$HOME/.history", use it whenever
2734 		 * "HISTFILE" is not present.
2735 		 */
2736 		hfilename = concat(inithome, slash, historyname, (char *)NULL);
2737 		if (flag == HF_SAVE && !ev_eql(savehistname, on))
2738 			return (NULL);
2739 	}
2740 	return (hfilename);
2741 }
2742 
2743 /*
2744  * Save the history by writing to ~/.history
2745  */
2746 EXPORT void
save_history(flg)2747 save_history(flg)
2748 	int flg;
2749 {
2750 	MYFILE	*f;
2751 	char	*hfname;
2752 
2753 	if (no_lines == 0)	/* don't damage history File */
2754 		return;
2755 	hfname = get_histfname(HF_SAVE);
2756 	if (hfname == NULL)
2757 		return;
2758 	f = fileopen(hfname, for_wct);
2759 	if (f) {
2760 		put_history(f, flg | HI_ANSI_NL, 0, 0, NULL);
2761 		fclose(f);
2762 	}
2763 }
2764 
2765 
2766 /*
2767  * Init the history by reading from ~/.history
2768  */
2769 EXPORT void
read_init_history()2770 read_init_history()
2771 {
2772 	MYFILE	*f;
2773 	char	*hfname;
2774 
2775 	hfname = get_histfname(HF_READ);
2776 	if (hfname == NULL)
2777 		return;
2778 	f = fileopen(hfname, for_read);
2779 	if (f) {
2780 		readhistory(f);
2781 		fclose(f);
2782 	}
2783 }
2784 
2785 /*
2786  * Read in the history from a file.
2787  * Used to init history and for source -h
2788  */
2789 EXPORT void
readhistory(f)2790 readhistory(f)
2791 	register MYFILE	*f;
2792 {
2793 #define	BUF_SIZE	8192		/* XXX Dymanic resize ???	*/
2794 		char	rbuf[BUF_SIZE+1]; /* + space for null byte	*/
2795 	register char	*s = rbuf;	/* Start of current line	*/
2796 	register char	*ep;		/* End of current line		*/
2797 	register int	len;
2798 	register int	amt;
2799 
2800 	amt = BUF_SIZE;
2801 	rbuf[amt] = '\0';		/* Final null byte after rbuf */
2802 	while ((amt = fileread(f, s, amt)) > 0) {
2803 		amt += s - rbuf;	/* Continue to work on whole rest */
2804 		s = rbuf;
2805 
2806 	again:
2807 		ep = strchr(s, '\n');
2808 		if (ep == NULL && s > rbuf && amt >= BUF_SIZE) {
2809 			/*
2810 			 * If no '\n' could be found, we need to check whether
2811 			 * we are in the middle of a line. If the buffer was
2812 			 * not full, we are at EOF already.
2813 			 */
2814 			amt = amt - (s - rbuf);	/* Compute unprocessed amt  */
2815 			movebytes(s, rbuf, amt); /* Move to the start of buf */
2816 			s = &rbuf[amt];		/* Point past old content   */
2817 			amt = BUF_SIZE - amt;	/* Compute remaining space  */
2818 			continue;		/* Read again to fill buf   */
2819 		}
2820 		if (ep)				/* Current line ends in '\n' */
2821 			*ep = '\0';		/* so clear it		    */
2822 
2823 		/*
2824 		 * Skip bash timestamps
2825 		 */
2826 		if (s[0] == '#' && s[1] == '+') {
2827 			register char	*p;
2828 
2829 			for (p = &s[2]; *p != '\0'; p++)
2830 				if (!_isdigit((unsigned char)*p))
2831 					break;
2832 			if (*p == '\0')
2833 				goto endline;
2834 		}
2835 
2836 #ifdef	USE_ANSI_NL_SEPARATOR
2837 		len = strlen(s);
2838 #else
2839 		{
2840 			char *cp;
2841 			for (cp = s, len = 0; *cp; cp++, len++) {
2842 				if (*cp == '\205')
2843 					*cp = '\n';
2844 			}
2845 		}
2846 #endif
2847 #ifdef	DEBUG
2848 		fprintf(stderr, "appending: %d bytes: %s\r\n", len, s);
2849 #endif
2850 		append_line(s, (unsigned)len+1, len);
2851 
2852 	endline:
2853 		if (ep) {			/* Found '\n', check rest */
2854 			s = &ep[1];
2855 			if ((s - rbuf) >= amt && amt < BUF_SIZE) /* EOF */
2856 				break;
2857 			goto again;
2858 		} else {
2859 			if (amt < BUF_SIZE)			/* EOF */
2860 				break;
2861 			s = rbuf;
2862 			amt = BUF_SIZE;
2863 		}
2864 	}
2865 }
2866 
2867 #include <schily/termcap.h>
2868 LOCAL	char	*KE;		/* Keypad end transmit mode	"ke"	*/
2869 LOCAL	char	*KS;		/* Keypad start transmit mode	"ks"	*/
2870 LOCAL	char	*VE;		/* Visual end sequence		"ve"	*/
2871 LOCAL	char	*VS;		/* Visual start sequence	"vs"	*/
2872 
2873 LOCAL	char	**tstrs[] = {
2874 		&KE, &KS, &VE, &VS,
2875 };
2876 
2877 LOCAL void
term_init()2878 term_init()
2879 {
2880 	register char	*np = "keksvevs";
2881 	register char	***sp = tstrs;
2882 
2883 	if (KE) free(KE);
2884 	if (KS) free(KS);
2885 	if (VE) free(VE);
2886 	if (VS) free(VS);
2887 	do {
2888 		*(*sp++) = tgetstr(np, NULL);
2889 		np += 2;
2890 	} while (*np);
2891 }
2892 
2893 #define	f_putch		((int (*)__PR((int)))putch)
2894 
2895 LOCAL void
tty_init()2896 tty_init()
2897 {
2898 	tputs(VS, 0, f_putch);	/* make cursor visible */
2899 	tputs(KS, 0, f_putch);	/* start keypad transmit mode */
2900 }
2901 
2902 LOCAL void
tty_term()2903 tty_term()
2904 {
2905 	tputs(VE, 0, f_putch);
2906 	tputs(KE, 0, f_putch);
2907 	bflush();
2908 }
2909 
2910 
2911 #ifdef	DO_DEBUG
2912 #include <schily/varargs.h>
2913 /*
2914  * Do formatted debugging output to the console
2915  */
2916 /* PRINTFLIKE1 */
2917 #ifdef	PROTOTYPES
2918 LOCAL void
cdbg(char * fmt,...)2919 cdbg(char *fmt, ...)
2920 #else
2921 LOCAL void
2922 cdbg(fmt, va_alist)
2923 	char	*fmt;
2924 	va_dcl
2925 #endif
2926 {
2927 	char	lbuf[1024];
2928 	va_list	args;
2929 	static	int	f;
2930 	int	len;
2931 
2932 #ifdef	PROTOTYPES
2933 	va_start(args, fmt);
2934 #else
2935 	va_start(args);
2936 #endif
2937 #ifdef	HAVE_VSNPRINTF
2938 	len = vsnprintf(lbuf, sizeof (lbuf), fmt, args);
2939 #else
2940 	len = snprintf(lbuf, sizeof (lbuf), "%r", fmt, args);
2941 #endif
2942 	va_end(args);
2943 
2944 	if (f == 0) {
2945 		char	*cname;
2946 		if ((cname = getcurenv("BSH_DBGTERM")) == NULL)
2947 			cname = "/dev/console";
2948 
2949 		f = open(cname, O_WRONLY);
2950 		if (f == 0)
2951 			return;
2952 #ifdef	F_SETFD
2953 		fcntl(f, F_SETFD, FD_CLOEXEC);	/* Set close on exec */
2954 #endif
2955 	}
2956 	write(f, lbuf, len);
2957 }
2958 #endif	/* DO_DEBUG */
2959 
2960 #endif /* INTERACTIVE */
2961