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