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