1/*	$NetBSD: chared.c,v 1.49 2016/02/24 14:29:21 christos 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. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include "config.h"
36#if !defined(lint) && !defined(SCCSID)
37#if 0
38static char sccsid[] = "@(#)chared.c	8.1 (Berkeley) 6/4/93";
39#else
40__RCSID("$NetBSD: chared.c,v 1.49 2016/02/24 14:29:21 christos Exp $");
41#endif
42#endif /* not lint && not SCCSID */
43#include <sys/cdefs.h>
44__FBSDID("$FreeBSD: stable/11/lib/libedit/chared.c 313981 2017-02-20 03:33:59Z pfg $");
45
46/*
47 * chared.c: Character editor utilities
48 */
49#include <ctype.h>
50#include <stdlib.h>
51#include <string.h>
52
53#include "el.h"
54#include "common.h"
55
56private void ch__clearmacro (EditLine *);
57
58/* value to leave unused in line buffer */
59#define	EL_LEAVE	2
60
61/* cv_undo():
62 *	Handle state for the vi undo command
63 */
64protected void
65cv_undo(EditLine *el)
66{
67	c_undo_t *vu = &el->el_chared.c_undo;
68	c_redo_t *r = &el->el_chared.c_redo;
69	size_t size;
70
71	/* Save entire line for undo */
72	size = (size_t)(el->el_line.lastchar - el->el_line.buffer);
73	vu->len = (ssize_t)size;
74	vu->cursor = (int)(el->el_line.cursor - el->el_line.buffer);
75	(void)memcpy(vu->buf, el->el_line.buffer, size * sizeof(*vu->buf));
76
77	/* save command info for redo */
78	r->count = el->el_state.doingarg ? el->el_state.argument : 0;
79	r->action = el->el_chared.c_vcmd.action;
80	r->pos = r->buf;
81	r->cmd = el->el_state.thiscmd;
82	r->ch = el->el_state.thisch;
83}
84
85/* cv_yank():
86 *	Save yank/delete data for paste
87 */
88protected void
89cv_yank(EditLine *el, const Char *ptr, int size)
90{
91	c_kill_t *k = &el->el_chared.c_kill;
92
93	(void)memcpy(k->buf, ptr, (size_t)size * sizeof(*k->buf));
94	k->last = k->buf + size;
95}
96
97
98/* c_insert():
99 *	Insert num characters
100 */
101protected void
102c_insert(EditLine *el, int num)
103{
104	Char *cp;
105
106	if (el->el_line.lastchar + num >= el->el_line.limit) {
107		if (!ch_enlargebufs(el, (size_t)num))
108			return;		/* can't go past end of buffer */
109	}
110
111	if (el->el_line.cursor < el->el_line.lastchar) {
112		/* if I must move chars */
113		for (cp = el->el_line.lastchar; cp >= el->el_line.cursor; cp--)
114			cp[num] = *cp;
115	}
116	el->el_line.lastchar += num;
117}
118
119
120/* c_delafter():
121 *	Delete num characters after the cursor
122 */
123protected void
124c_delafter(EditLine *el, int num)
125{
126
127	if (el->el_line.cursor + num > el->el_line.lastchar)
128		num = (int)(el->el_line.lastchar - el->el_line.cursor);
129
130	if (el->el_map.current != el->el_map.emacs) {
131		cv_undo(el);
132		cv_yank(el, el->el_line.cursor, num);
133	}
134
135	if (num > 0) {
136		Char *cp;
137
138		for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++)
139			*cp = cp[num];
140
141		el->el_line.lastchar -= num;
142	}
143}
144
145
146/* c_delafter1():
147 *	Delete the character after the cursor, do not yank
148 */
149protected void
150c_delafter1(EditLine *el)
151{
152	Char *cp;
153
154	for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++)
155		*cp = cp[1];
156
157	el->el_line.lastchar--;
158}
159
160
161/* c_delbefore():
162 *	Delete num characters before the cursor
163 */
164protected void
165c_delbefore(EditLine *el, int num)
166{
167
168	if (el->el_line.cursor - num < el->el_line.buffer)
169		num = (int)(el->el_line.cursor - el->el_line.buffer);
170
171	if (el->el_map.current != el->el_map.emacs) {
172		cv_undo(el);
173		cv_yank(el, el->el_line.cursor - num, num);
174	}
175
176	if (num > 0) {
177		Char *cp;
178
179		for (cp = el->el_line.cursor - num;
180		    cp <= el->el_line.lastchar;
181		    cp++)
182			*cp = cp[num];
183
184		el->el_line.lastchar -= num;
185	}
186}
187
188
189/* c_delbefore1():
190 *	Delete the character before the cursor, do not yank
191 */
192protected void
193c_delbefore1(EditLine *el)
194{
195	Char *cp;
196
197	for (cp = el->el_line.cursor - 1; cp <= el->el_line.lastchar; cp++)
198		*cp = cp[1];
199
200	el->el_line.lastchar--;
201}
202
203
204/* ce__isword():
205 *	Return if p is part of a word according to emacs
206 */
207protected int
208ce__isword(wint_t p)
209{
210	return Isalnum(p) || Strchr(STR("*?_-.[]~="), p) != NULL;
211}
212
213
214/* cv__isword():
215 *	Return if p is part of a word according to vi
216 */
217protected int
218cv__isword(wint_t p)
219{
220	if (Isalnum(p) || p == '_')
221		return 1;
222	if (Isgraph(p))
223		return 2;
224	return 0;
225}
226
227
228/* cv__isWord():
229 *	Return if p is part of a big word according to vi
230 */
231protected int
232cv__isWord(wint_t p)
233{
234	return !Isspace(p);
235}
236
237
238/* c__prev_word():
239 *	Find the previous word
240 */
241protected Char *
242c__prev_word(Char *p, Char *low, int n, int (*wtest)(wint_t))
243{
244	p--;
245
246	while (n--) {
247		while ((p >= low) && !(*wtest)(*p))
248			p--;
249		while ((p >= low) && (*wtest)(*p))
250			p--;
251	}
252
253	/* cp now points to one character before the word */
254	p++;
255	if (p < low)
256		p = low;
257	/* cp now points where we want it */
258	return p;
259}
260
261
262/* c__next_word():
263 *	Find the next word
264 */
265protected Char *
266c__next_word(Char *p, Char *high, int n, int (*wtest)(wint_t))
267{
268	while (n--) {
269		while ((p < high) && !(*wtest)(*p))
270			p++;
271		while ((p < high) && (*wtest)(*p))
272			p++;
273	}
274	if (p > high)
275		p = high;
276	/* p now points where we want it */
277	return p;
278}
279
280/* cv_next_word():
281 *	Find the next word vi style
282 */
283protected Char *
284cv_next_word(EditLine *el, Char *p, Char *high, int n, int (*wtest)(wint_t))
285{
286	int test;
287
288	while (n--) {
289		test = (*wtest)(*p);
290		while ((p < high) && (*wtest)(*p) == test)
291			p++;
292		/*
293		 * vi historically deletes with cw only the word preserving the
294		 * trailing whitespace! This is not what 'w' does..
295		 */
296		if (n || el->el_chared.c_vcmd.action != (DELETE|INSERT))
297			while ((p < high) && Isspace(*p))
298				p++;
299	}
300
301	/* p now points where we want it */
302	if (p > high)
303		return high;
304	else
305		return p;
306}
307
308
309/* cv_prev_word():
310 *	Find the previous word vi style
311 */
312protected Char *
313cv_prev_word(Char *p, Char *low, int n, int (*wtest)(wint_t))
314{
315	int test;
316
317	p--;
318	while (n--) {
319		while ((p > low) && Isspace(*p))
320			p--;
321		test = (*wtest)(*p);
322		while ((p >= low) && (*wtest)(*p) == test)
323			p--;
324	}
325	p++;
326
327	/* p now points where we want it */
328	if (p < low)
329		return low;
330	else
331		return p;
332}
333
334
335/* cv_delfini():
336 *	Finish vi delete action
337 */
338protected void
339cv_delfini(EditLine *el)
340{
341	int size;
342	int action = el->el_chared.c_vcmd.action;
343
344	if (action & INSERT)
345		el->el_map.current = el->el_map.key;
346
347	if (el->el_chared.c_vcmd.pos == 0)
348		/* sanity */
349		return;
350
351	size = (int)(el->el_line.cursor - el->el_chared.c_vcmd.pos);
352	if (size == 0)
353		size = 1;
354	el->el_line.cursor = el->el_chared.c_vcmd.pos;
355	if (action & YANK) {
356		if (size > 0)
357			cv_yank(el, el->el_line.cursor, size);
358		else
359			cv_yank(el, el->el_line.cursor + size, -size);
360	} else {
361		if (size > 0) {
362			c_delafter(el, size);
363			re_refresh_cursor(el);
364		} else  {
365			c_delbefore(el, -size);
366			el->el_line.cursor += size;
367		}
368	}
369	el->el_chared.c_vcmd.action = NOP;
370}
371
372
373/* cv__endword():
374 *	Go to the end of this word according to vi
375 */
376protected Char *
377cv__endword(Char *p, Char *high, int n, int (*wtest)(wint_t))
378{
379	int test;
380
381	p++;
382
383	while (n--) {
384		while ((p < high) && Isspace(*p))
385			p++;
386
387		test = (*wtest)(*p);
388		while ((p < high) && (*wtest)(*p) == test)
389			p++;
390	}
391	p--;
392	return p;
393}
394
395/* ch_init():
396 *	Initialize the character editor
397 */
398protected int
399ch_init(EditLine *el)
400{
401	c_macro_t *ma = &el->el_chared.c_macro;
402
403	el->el_line.buffer		= el_malloc(EL_BUFSIZ *
404	    sizeof(*el->el_line.buffer));
405	if (el->el_line.buffer == NULL)
406		return -1;
407
408	(void) memset(el->el_line.buffer, 0, EL_BUFSIZ *
409	    sizeof(*el->el_line.buffer));
410	el->el_line.cursor		= el->el_line.buffer;
411	el->el_line.lastchar		= el->el_line.buffer;
412	el->el_line.limit		= &el->el_line.buffer[EL_BUFSIZ - EL_LEAVE];
413
414	el->el_chared.c_undo.buf	= el_malloc(EL_BUFSIZ *
415	    sizeof(*el->el_chared.c_undo.buf));
416	if (el->el_chared.c_undo.buf == NULL)
417		return -1;
418	(void) memset(el->el_chared.c_undo.buf, 0, EL_BUFSIZ *
419	    sizeof(*el->el_chared.c_undo.buf));
420	el->el_chared.c_undo.len	= -1;
421	el->el_chared.c_undo.cursor	= 0;
422	el->el_chared.c_redo.buf	= el_malloc(EL_BUFSIZ *
423	    sizeof(*el->el_chared.c_redo.buf));
424	if (el->el_chared.c_redo.buf == NULL)
425		return -1;
426	el->el_chared.c_redo.pos	= el->el_chared.c_redo.buf;
427	el->el_chared.c_redo.lim	= el->el_chared.c_redo.buf + EL_BUFSIZ;
428	el->el_chared.c_redo.cmd	= ED_UNASSIGNED;
429
430	el->el_chared.c_vcmd.action	= NOP;
431	el->el_chared.c_vcmd.pos	= el->el_line.buffer;
432
433	el->el_chared.c_kill.buf	= el_malloc(EL_BUFSIZ *
434	    sizeof(*el->el_chared.c_kill.buf));
435	if (el->el_chared.c_kill.buf == NULL)
436		return -1;
437	(void) memset(el->el_chared.c_kill.buf, 0, EL_BUFSIZ *
438	    sizeof(*el->el_chared.c_kill.buf));
439	el->el_chared.c_kill.mark	= el->el_line.buffer;
440	el->el_chared.c_kill.last	= el->el_chared.c_kill.buf;
441	el->el_chared.c_resizefun	= NULL;
442	el->el_chared.c_resizearg	= NULL;
443	el->el_chared.c_aliasfun	= NULL;
444	el->el_chared.c_aliasarg	= NULL;
445
446	el->el_map.current		= el->el_map.key;
447
448	el->el_state.inputmode		= MODE_INSERT; /* XXX: save a default */
449	el->el_state.doingarg		= 0;
450	el->el_state.metanext		= 0;
451	el->el_state.argument		= 1;
452	el->el_state.lastcmd		= ED_UNASSIGNED;
453
454	ma->level	= -1;
455	ma->offset	= 0;
456	ma->macro	= el_malloc(EL_MAXMACRO * sizeof(*ma->macro));
457	if (ma->macro == NULL)
458		return -1;
459	return 0;
460}
461
462/* ch_reset():
463 *	Reset the character editor
464 */
465protected void
466ch_reset(EditLine *el, int mclear)
467{
468	el->el_line.cursor		= el->el_line.buffer;
469	el->el_line.lastchar		= el->el_line.buffer;
470
471	el->el_chared.c_undo.len	= -1;
472	el->el_chared.c_undo.cursor	= 0;
473
474	el->el_chared.c_vcmd.action	= NOP;
475	el->el_chared.c_vcmd.pos	= el->el_line.buffer;
476
477	el->el_chared.c_kill.mark	= el->el_line.buffer;
478
479	el->el_map.current		= el->el_map.key;
480
481	el->el_state.inputmode		= MODE_INSERT; /* XXX: save a default */
482	el->el_state.doingarg		= 0;
483	el->el_state.metanext		= 0;
484	el->el_state.argument		= 1;
485	el->el_state.lastcmd		= ED_UNASSIGNED;
486
487	el->el_history.eventno		= 0;
488
489	if (mclear)
490		ch__clearmacro(el);
491}
492
493private void
494ch__clearmacro(EditLine *el)
495{
496	c_macro_t *ma = &el->el_chared.c_macro;
497	while (ma->level >= 0)
498		el_free(ma->macro[ma->level--]);
499}
500
501/* ch_enlargebufs():
502 *	Enlarge line buffer to be able to hold twice as much characters.
503 *	Returns 1 if successful, 0 if not.
504 */
505protected int
506ch_enlargebufs(EditLine *el, size_t addlen)
507{
508	size_t sz, newsz;
509	Char *newbuffer, *oldbuf, *oldkbuf;
510
511	sz = (size_t)(el->el_line.limit - el->el_line.buffer + EL_LEAVE);
512	newsz = sz * 2;
513	/*
514	 * If newly required length is longer than current buffer, we need
515	 * to make the buffer big enough to hold both old and new stuff.
516	 */
517	if (addlen > sz) {
518		while(newsz - sz < addlen)
519			newsz *= 2;
520	}
521
522	/*
523	 * Reallocate line buffer.
524	 */
525	newbuffer = el_realloc(el->el_line.buffer, newsz * sizeof(*newbuffer));
526	if (!newbuffer)
527		return 0;
528
529	/* zero the newly added memory, leave old data in */
530	(void) memset(&newbuffer[sz], 0, (newsz - sz) * sizeof(*newbuffer));
531
532	oldbuf = el->el_line.buffer;
533
534	el->el_line.buffer = newbuffer;
535	el->el_line.cursor = newbuffer + (el->el_line.cursor - oldbuf);
536	el->el_line.lastchar = newbuffer + (el->el_line.lastchar - oldbuf);
537	/* don't set new size until all buffers are enlarged */
538	el->el_line.limit  = &newbuffer[sz - EL_LEAVE];
539
540	/*
541	 * Reallocate kill buffer.
542	 */
543	newbuffer = el_realloc(el->el_chared.c_kill.buf, newsz *
544	    sizeof(*newbuffer));
545	if (!newbuffer)
546		return 0;
547
548	/* zero the newly added memory, leave old data in */
549	(void) memset(&newbuffer[sz], 0, (newsz - sz) * sizeof(*newbuffer));
550
551	oldkbuf = el->el_chared.c_kill.buf;
552
553	el->el_chared.c_kill.buf = newbuffer;
554	el->el_chared.c_kill.last = newbuffer +
555					(el->el_chared.c_kill.last - oldkbuf);
556	el->el_chared.c_kill.mark = el->el_line.buffer +
557					(el->el_chared.c_kill.mark - oldbuf);
558
559	/*
560	 * Reallocate undo buffer.
561	 */
562	newbuffer = el_realloc(el->el_chared.c_undo.buf,
563	    newsz * sizeof(*newbuffer));
564	if (!newbuffer)
565		return 0;
566
567	/* zero the newly added memory, leave old data in */
568	(void) memset(&newbuffer[sz], 0, (newsz - sz) * sizeof(*newbuffer));
569	el->el_chared.c_undo.buf = newbuffer;
570
571	newbuffer = el_realloc(el->el_chared.c_redo.buf,
572	    newsz * sizeof(*newbuffer));
573	if (!newbuffer)
574		return 0;
575	el->el_chared.c_redo.pos = newbuffer +
576			(el->el_chared.c_redo.pos - el->el_chared.c_redo.buf);
577	el->el_chared.c_redo.lim = newbuffer +
578			(el->el_chared.c_redo.lim - el->el_chared.c_redo.buf);
579	el->el_chared.c_redo.buf = newbuffer;
580
581	if (!hist_enlargebuf(el, sz, newsz))
582		return 0;
583
584	/* Safe to set enlarged buffer size */
585	el->el_line.limit  = &el->el_line.buffer[newsz - EL_LEAVE];
586	if (el->el_chared.c_resizefun)
587		(*el->el_chared.c_resizefun)(el, el->el_chared.c_resizearg);
588	return 1;
589}
590
591/* ch_end():
592 *	Free the data structures used by the editor
593 */
594protected void
595ch_end(EditLine *el)
596{
597	el_free(el->el_line.buffer);
598	el->el_line.buffer = NULL;
599	el->el_line.limit = NULL;
600	el_free(el->el_chared.c_undo.buf);
601	el->el_chared.c_undo.buf = NULL;
602	el_free(el->el_chared.c_redo.buf);
603	el->el_chared.c_redo.buf = NULL;
604	el->el_chared.c_redo.pos = NULL;
605	el->el_chared.c_redo.lim = NULL;
606	el->el_chared.c_redo.cmd = ED_UNASSIGNED;
607	el_free(el->el_chared.c_kill.buf);
608	el->el_chared.c_kill.buf = NULL;
609	ch_reset(el, 1);
610	el_free(el->el_chared.c_macro.macro);
611	el->el_chared.c_macro.macro = NULL;
612}
613
614
615/* el_insertstr():
616 *	Insert string at cursorI
617 */
618public int
619FUN(el,insertstr)(EditLine *el, const Char *s)
620{
621	size_t len;
622
623	if (s == NULL || (len = Strlen(s)) == 0)
624		return -1;
625	if (el->el_line.lastchar + len >= el->el_line.limit) {
626		if (!ch_enlargebufs(el, len))
627			return -1;
628	}
629
630	c_insert(el, (int)len);
631	while (*s)
632		*el->el_line.cursor++ = *s++;
633	return 0;
634}
635
636
637/* el_deletestr():
638 *	Delete num characters before the cursor
639 */
640public void
641el_deletestr(EditLine *el, int n)
642{
643	if (n <= 0)
644		return;
645
646	if (el->el_line.cursor < &el->el_line.buffer[n])
647		return;
648
649	c_delbefore(el, n);		/* delete before dot */
650	el->el_line.cursor -= n;
651	if (el->el_line.cursor < el->el_line.buffer)
652		el->el_line.cursor = el->el_line.buffer;
653}
654
655/* el_cursor():
656 *	Move the cursor to the left or the right of the current position
657 */
658public int
659el_cursor(EditLine *el, int n)
660{
661	if (n == 0)
662		goto out;
663
664	el->el_line.cursor += n;
665
666	if (el->el_line.cursor < el->el_line.buffer)
667		el->el_line.cursor = el->el_line.buffer;
668	if (el->el_line.cursor > el->el_line.lastchar)
669		el->el_line.cursor = el->el_line.lastchar;
670out:
671	return (int)(el->el_line.cursor - el->el_line.buffer);
672}
673
674/* c_gets():
675 *	Get a string
676 */
677protected int
678c_gets(EditLine *el, Char *buf, const Char *prompt)
679{
680	wchar_t wch;
681	ssize_t len;
682	Char *cp = el->el_line.buffer, ch;
683
684	if (prompt) {
685		len = (ssize_t)Strlen(prompt);
686		(void)memcpy(cp, prompt, (size_t)len * sizeof(*cp));
687		cp += len;
688	}
689	len = 0;
690
691	for (;;) {
692		el->el_line.cursor = cp;
693		*cp = ' ';
694		el->el_line.lastchar = cp + 1;
695		re_refresh(el);
696
697		if (el_wgetc(el, &wch) != 1) {
698			ed_end_of_file(el, 0);
699			len = -1;
700			break;
701		}
702		ch = (Char)wch;
703
704		switch (ch) {
705
706		case L'\b':	/* Delete and backspace */
707		case 0177:
708			if (len == 0) {
709				len = -1;
710				break;
711			}
712			len--;
713			cp--;
714			continue;
715
716		case 0033:	/* ESC */
717		case L'\r':	/* Newline */
718		case L'\n':
719			buf[len] = ch;
720			break;
721
722		default:
723			if (len >= (ssize_t)(EL_BUFSIZ - 16))
724				terminal_beep(el);
725			else {
726				buf[len++] = ch;
727				*cp++ = ch;
728			}
729			continue;
730		}
731		break;
732	}
733
734	el->el_line.buffer[0] = '\0';
735	el->el_line.lastchar = el->el_line.buffer;
736	el->el_line.cursor = el->el_line.buffer;
737	return (int)len;
738}
739
740
741/* c_hpos():
742 *	Return the current horizontal position of the cursor
743 */
744protected int
745c_hpos(EditLine *el)
746{
747	Char *ptr;
748
749	/*
750	 * Find how many characters till the beginning of this line.
751	 */
752	if (el->el_line.cursor == el->el_line.buffer)
753		return 0;
754	else {
755		for (ptr = el->el_line.cursor - 1;
756		     ptr >= el->el_line.buffer && *ptr != '\n';
757		     ptr--)
758			continue;
759		return (int)(el->el_line.cursor - ptr - 1);
760	}
761}
762
763protected int
764ch_resizefun(EditLine *el, el_zfunc_t f, void *a)
765{
766	el->el_chared.c_resizefun = f;
767	el->el_chared.c_resizearg = a;
768	return 0;
769}
770
771protected int
772ch_aliasfun(EditLine *el, el_afunc_t f, void *a)
773{
774	el->el_chared.c_aliasfun = f;
775	el->el_chared.c_aliasarg = a;
776	return 0;
777}
778