xref: /openbsd-src/lib/libedit/chared.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: chared.c,v 1.8 2003/11/25 20:12:38 otto 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 #if !defined(lint) && !defined(SCCSID)
38 #if 0
39 static char sccsid[] = "@(#)chared.c	8.1 (Berkeley) 6/4/93";
40 #else
41 static const char rcsid[] = "$OpenBSD: chared.c,v 1.8 2003/11/25 20:12:38 otto Exp $";
42 #endif
43 #endif /* not lint && not SCCSID */
44 
45 /*
46  * chared.c: Character editor utilities
47  */
48 #include <stdlib.h>
49 #include "el.h"
50 
51 /* value to leave unused in line buffer */
52 #define	EL_LEAVE	2
53 
54 /* cv_undo():
55  *	Handle state for the vi undo command
56  */
57 protected void
58 cv_undo(EditLine *el)
59 {
60 	c_undo_t *vu = &el->el_chared.c_undo;
61 	c_redo_t *r = &el->el_chared.c_redo;
62 	uint size;
63 
64 	/* Save entire line for undo */
65 	size = el->el_line.lastchar - el->el_line.buffer;
66 	vu->len = size;
67 	vu->cursor = el->el_line.cursor - el->el_line.buffer;
68 	memcpy(vu->buf, el->el_line.buffer, size);
69 
70 	/* save command info for redo */
71 	r->count = el->el_state.doingarg ? el->el_state.argument : 0;
72 	r->action = el->el_chared.c_vcmd.action;
73 	r->pos = r->buf;
74 	r->cmd = el->el_state.thiscmd;
75 	r->ch = el->el_state.thisch;
76 }
77 
78 /* cv_yank():
79  *	Save yank/delete data for paste
80  */
81 protected void
82 cv_yank(EditLine *el, const char *ptr, int size)
83 {
84 	c_kill_t *k = &el->el_chared.c_kill;
85 
86 	memcpy(k->buf, ptr, size +0u);
87 	k->last = k->buf + size;
88 }
89 
90 
91 /* c_insert():
92  *	Insert num characters
93  */
94 protected void
95 c_insert(EditLine *el, int num)
96 {
97 	char *cp;
98 
99 	if (el->el_line.lastchar + num >= el->el_line.limit) {
100 		if (!ch_enlargebufs(el, num +0u))
101 			return;		/* can't go past end of buffer */
102 	}
103 
104 	if (el->el_line.cursor < el->el_line.lastchar) {
105 		/* if I must move chars */
106 		for (cp = el->el_line.lastchar; cp >= el->el_line.cursor; cp--)
107 			cp[num] = *cp;
108 	}
109 	el->el_line.lastchar += num;
110 }
111 
112 
113 /* c_delafter():
114  *	Delete num characters after the cursor
115  */
116 protected void
117 c_delafter(EditLine *el, int num)
118 {
119 
120 	if (el->el_line.cursor + num > el->el_line.lastchar)
121 		num = el->el_line.lastchar - el->el_line.cursor;
122 
123 	if (el->el_map.current != el->el_map.emacs) {
124 		cv_undo(el);
125 		cv_yank(el, el->el_line.cursor, num);
126 	}
127 
128 	if (num > 0) {
129 		char *cp;
130 
131 		for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++)
132 			*cp = cp[num];
133 
134 		el->el_line.lastchar -= num;
135 	}
136 }
137 
138 
139 /* c_delbefore():
140  *	Delete num characters before the cursor
141  */
142 protected void
143 c_delbefore(EditLine *el, int num)
144 {
145 
146 	if (el->el_line.cursor - num < el->el_line.buffer)
147 		num = el->el_line.cursor - el->el_line.buffer;
148 
149 	if (el->el_map.current != el->el_map.emacs) {
150 		cv_undo(el);
151 		cv_yank(el, el->el_line.cursor - num, num);
152 	}
153 
154 	if (num > 0) {
155 		char *cp;
156 
157 		for (cp = el->el_line.cursor - num;
158 		    cp <= el->el_line.lastchar;
159 		    cp++)
160 			*cp = cp[num];
161 
162 		el->el_line.lastchar -= num;
163 	}
164 }
165 
166 
167 /* ce__isword():
168  *	Return if p is part of a word according to emacs
169  */
170 protected int
171 ce__isword(int p)
172 {
173 	return (isalnum(p) || strchr("*?_-.[]~=", p) != NULL);
174 }
175 
176 
177 /* cv__isword():
178  *	Return if p is part of a word according to vi
179  */
180 protected int
181 cv__isword(int p)
182 {
183 	if (isalnum(p) || p == '_')
184 		return 1;
185 	if (isgraph(p))
186 		return 2;
187 	return 0;
188 }
189 
190 
191 /* cv__isWord():
192  *	Return if p is part of a big word according to vi
193  */
194 protected int
195 cv__isWord(int p)
196 {
197 	return (!isspace(p));
198 }
199 
200 
201 /* c__prev_word():
202  *	Find the previous word
203  */
204 protected char *
205 c__prev_word(char *p, char *low, int n, int (*wtest)(int))
206 {
207 	p--;
208 
209 	while (n--) {
210 		while ((p >= low) && !(*wtest)((unsigned char) *p))
211 			p--;
212 		while ((p >= low) && (*wtest)((unsigned char) *p))
213 			p--;
214 	}
215 
216 	/* cp now points to one character before the word */
217 	p++;
218 	if (p < low)
219 		p = low;
220 	/* cp now points where we want it */
221 	return (p);
222 }
223 
224 
225 /* c__next_word():
226  *	Find the next word
227  */
228 protected char *
229 c__next_word(char *p, char *high, int n, int (*wtest)(int))
230 {
231 	while (n--) {
232 		while ((p < high) && !(*wtest)((unsigned char) *p))
233 			p++;
234 		while ((p < high) && (*wtest)((unsigned char) *p))
235 			p++;
236 	}
237 	if (p > high)
238 		p = high;
239 	/* p now points where we want it */
240 	return (p);
241 }
242 
243 /* cv_next_word():
244  *	Find the next word vi style
245  */
246 protected char *
247 cv_next_word(EditLine *el, char *p, char *high, int n, int (*wtest)(int))
248 {
249 	int test;
250 
251 	while (n--) {
252 		test = (*wtest)((unsigned char) *p);
253 		while ((p < high) && (*wtest)((unsigned char) *p) == test)
254 			p++;
255 		/*
256 		 * vi historically deletes with cw only the word preserving the
257 		 * trailing whitespace! This is not what 'w' does..
258 		 */
259 		if (n || el->el_chared.c_vcmd.action != (DELETE|INSERT))
260 			while ((p < high) && isspace((unsigned char) *p))
261 				p++;
262 	}
263 
264 	/* p now points where we want it */
265 	if (p > high)
266 		return (high);
267 	else
268 		return (p);
269 }
270 
271 
272 /* cv_prev_word():
273  *	Find the previous word vi style
274  */
275 protected char *
276 cv_prev_word(char *p, char *low, int n, int (*wtest)(int))
277 {
278 	int test;
279 
280 	p--;
281 	while (n--) {
282 		while ((p > low) && isspace((unsigned char) *p))
283 			p--;
284 		test = (*wtest)((unsigned char) *p);
285 		while ((p >= low) && (*wtest)((unsigned char) *p) == test)
286 			p--;
287 	}
288 	p++;
289 
290 	/* p now points where we want it */
291 	if (p < low)
292 		return (low);
293 	else
294 		return (p);
295 }
296 
297 
298 #ifdef notdef
299 /* c__number():
300  *	Ignore character p points to, return number appearing after that.
301  * 	A '$' by itself means a big number; "$-" is for negative; '^' means 1.
302  * 	Return p pointing to last char used.
303  */
304 protected char *
305 c__number(
306     char *p,	/* character position */
307     int *num,	/* Return value	*/
308     int dval)	/* dval is the number to subtract from like $-3 */
309 {
310 	int i;
311 	int sign = 1;
312 
313 	if (*++p == '^') {
314 		*num = 1;
315 		return (p);
316 	}
317 	if (*p == '$') {
318 		if (*++p != '-') {
319 			*num = 0x7fffffff;	/* Handle $ */
320 			return (--p);
321 		}
322 		sign = -1;			/* Handle $- */
323 		++p;
324 	}
325 	for (i = 0; isdigit((unsigned char) *p); i = 10 * i + *p++ - '0')
326 		continue;
327 	*num = (sign < 0 ? dval - i : i);
328 	return (--p);
329 }
330 #endif
331 
332 /* cv_delfini():
333  *	Finish vi delete action
334  */
335 protected void
336 cv_delfini(EditLine *el)
337 {
338 	int size;
339 	int action = el->el_chared.c_vcmd.action;
340 
341 	if (action & INSERT)
342 		el->el_map.current = el->el_map.key;
343 
344 	if (el->el_chared.c_vcmd.pos == 0)
345 		/* sanity */
346 		return;
347 
348 	size = el->el_line.cursor - el->el_chared.c_vcmd.pos;
349 	if (size == 0)
350 		size = 1;
351 	el->el_line.cursor = el->el_chared.c_vcmd.pos;
352 	if (action & YANK) {
353 		if (size > 0)
354 			cv_yank(el, el->el_line.cursor, size);
355 		else
356 			cv_yank(el, el->el_line.cursor + size, -size);
357 	} else {
358 		if (size > 0) {
359 			c_delafter(el, size);
360 			re_refresh_cursor(el);
361 		} else  {
362 			c_delbefore(el, -size);
363 			el->el_line.cursor += size;
364 		}
365 	}
366 	el->el_chared.c_vcmd.action = NOP;
367 }
368 
369 
370 #ifdef notdef
371 /* ce__endword():
372  *	Go to the end of this word according to emacs
373  */
374 protected char *
375 ce__endword(char *p, char *high, int n)
376 {
377 	p++;
378 
379 	while (n--) {
380 		while ((p < high) && isspace((unsigned char) *p))
381 			p++;
382 		while ((p < high) && !isspace((unsigned char) *p))
383 			p++;
384 	}
385 
386 	p--;
387 	return (p);
388 }
389 #endif
390 
391 
392 /* cv__endword():
393  *	Go to the end of this word according to vi
394  */
395 protected char *
396 cv__endword(char *p, char *high, int n, int (*wtest)(int))
397 {
398 	int test;
399 
400 	p++;
401 
402 	while (n--) {
403 		while ((p < high) && isspace((unsigned char) *p))
404 			p++;
405 
406 		test = (*wtest)((unsigned char) *p);
407 		while ((p < high) && (*wtest)((unsigned char) *p) == test)
408 			p++;
409 	}
410 	p--;
411 	return (p);
412 }
413 
414 /* ch_init():
415  *	Initialize the character editor
416  */
417 protected int
418 ch_init(EditLine *el)
419 {
420 	el->el_line.buffer		= (char *) el_malloc(EL_BUFSIZ);
421 	if (el->el_line.buffer == NULL)
422 		return (-1);
423 
424 	(void) memset(el->el_line.buffer, 0, EL_BUFSIZ);
425 	el->el_line.cursor		= el->el_line.buffer;
426 	el->el_line.lastchar		= el->el_line.buffer;
427 	el->el_line.limit		= &el->el_line.buffer[EL_BUFSIZ - EL_LEAVE];
428 
429 	el->el_chared.c_undo.buf	= (char *) el_malloc(EL_BUFSIZ);
430 	if (el->el_chared.c_undo.buf == NULL)
431 		return (-1);
432 	(void) memset(el->el_chared.c_undo.buf, 0, EL_BUFSIZ);
433 	el->el_chared.c_undo.len	= -1;
434 	el->el_chared.c_undo.cursor	= 0;
435 	el->el_chared.c_redo.buf	= (char *) el_malloc(EL_BUFSIZ);
436 	if (el->el_chared.c_redo.buf == NULL)
437 		return (-1);
438 	el->el_chared.c_redo.pos	= el->el_chared.c_redo.buf;
439 	el->el_chared.c_redo.lim	= el->el_chared.c_redo.buf + EL_BUFSIZ;
440 	el->el_chared.c_redo.cmd	= ED_UNASSIGNED;
441 
442 	el->el_chared.c_vcmd.action	= NOP;
443 	el->el_chared.c_vcmd.pos	= el->el_line.buffer;
444 
445 	el->el_chared.c_kill.buf	= (char *) el_malloc(EL_BUFSIZ);
446 	if (el->el_chared.c_kill.buf == NULL)
447 		return (-1);
448 	(void) memset(el->el_chared.c_kill.buf, 0, EL_BUFSIZ);
449 	el->el_chared.c_kill.mark	= el->el_line.buffer;
450 	el->el_chared.c_kill.last	= el->el_chared.c_kill.buf;
451 
452 	el->el_map.current		= el->el_map.key;
453 
454 	el->el_state.inputmode		= MODE_INSERT; /* XXX: save a default */
455 	el->el_state.doingarg		= 0;
456 	el->el_state.metanext		= 0;
457 	el->el_state.argument		= 1;
458 	el->el_state.lastcmd		= ED_UNASSIGNED;
459 
460 	el->el_chared.c_macro.level	= -1;
461 	el->el_chared.c_macro.offset	= 0;
462 	el->el_chared.c_macro.macro	= (char **) el_malloc(EL_MAXMACRO *
463 	    sizeof(char *));
464 	if (el->el_chared.c_macro.macro == NULL)
465 		return (-1);
466 	return (0);
467 }
468 
469 /* ch_reset():
470  *	Reset the character editor
471  */
472 protected void
473 ch_reset(EditLine *el)
474 {
475 	el->el_line.cursor		= el->el_line.buffer;
476 	el->el_line.lastchar		= el->el_line.buffer;
477 
478 	el->el_chared.c_undo.len	= -1;
479 	el->el_chared.c_undo.cursor	= 0;
480 
481 	el->el_chared.c_vcmd.action	= NOP;
482 	el->el_chared.c_vcmd.pos	= el->el_line.buffer;
483 
484 	el->el_chared.c_kill.mark	= el->el_line.buffer;
485 
486 	el->el_map.current		= el->el_map.key;
487 
488 	el->el_state.inputmode		= MODE_INSERT; /* XXX: save a default */
489 	el->el_state.doingarg		= 0;
490 	el->el_state.metanext		= 0;
491 	el->el_state.argument		= 1;
492 	el->el_state.lastcmd		= ED_UNASSIGNED;
493 
494 	el->el_chared.c_macro.level	= -1;
495 
496 	el->el_history.eventno		= 0;
497 }
498 
499 /* ch_enlargebufs():
500  *	Enlarge line buffer to be able to hold twice as much characters.
501  *	Returns 1 if successful, 0 if not.
502  */
503 protected int
504 ch_enlargebufs(el, addlen)
505 	EditLine *el;
506 	size_t addlen;
507 {
508 	size_t sz, newsz;
509 	char *newbuffer, *oldbuf, *oldkbuf;
510 
511 	sz = 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);
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);
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 	if (!newbuffer)
545 		return 0;
546 
547 	/* zero the newly added memory, leave old data in */
548 	(void) memset(&newbuffer[sz], 0, newsz - sz);
549 
550 	oldkbuf = el->el_chared.c_kill.buf;
551 
552 	el->el_chared.c_kill.buf = newbuffer;
553 	el->el_chared.c_kill.last = newbuffer +
554 					(el->el_chared.c_kill.last - oldkbuf);
555 	el->el_chared.c_kill.mark = el->el_line.buffer +
556 					(el->el_chared.c_kill.mark - oldbuf);
557 
558 	/*
559 	 * Reallocate undo buffer.
560 	 */
561 	newbuffer = el_realloc(el->el_chared.c_undo.buf, newsz);
562 	if (!newbuffer)
563 		return 0;
564 
565 	/* zero the newly added memory, leave old data in */
566 	(void) memset(&newbuffer[sz], 0, newsz - sz);
567 	el->el_chared.c_undo.buf = newbuffer;
568 
569 	newbuffer = el_realloc(el->el_chared.c_redo.buf, newsz);
570 	if (!newbuffer)
571 		return 0;
572 	el->el_chared.c_redo.pos = newbuffer +
573 			(el->el_chared.c_redo.pos - el->el_chared.c_redo.buf);
574 	el->el_chared.c_redo.lim = newbuffer +
575 			(el->el_chared.c_redo.lim - el->el_chared.c_redo.buf);
576 	el->el_chared.c_redo.buf = newbuffer;
577 
578 	if (!hist_enlargebuf(el, sz, newsz))
579 		return 0;
580 
581 	/* Safe to set enlarged buffer size */
582 	el->el_line.limit  = &el->el_line.buffer[newsz - EL_LEAVE];
583 	return 1;
584 }
585 
586 /* ch_end():
587  *	Free the data structures used by the editor
588  */
589 protected void
590 ch_end(EditLine *el)
591 {
592 	el_free((ptr_t) el->el_line.buffer);
593 	el->el_line.buffer = NULL;
594 	el->el_line.limit = NULL;
595 	el_free((ptr_t) el->el_chared.c_undo.buf);
596 	el->el_chared.c_undo.buf = NULL;
597 	el_free((ptr_t) el->el_chared.c_redo.buf);
598 	el->el_chared.c_redo.buf = NULL;
599 	el->el_chared.c_redo.pos = NULL;
600 	el->el_chared.c_redo.lim = NULL;
601 	el->el_chared.c_redo.cmd = ED_UNASSIGNED;
602 	el_free((ptr_t) el->el_chared.c_kill.buf);
603 	el->el_chared.c_kill.buf = NULL;
604 	el_free((ptr_t) el->el_chared.c_macro.macro);
605 	el->el_chared.c_macro.macro = NULL;
606 	ch_reset(el);
607 }
608 
609 
610 /* el_insertstr():
611  *	Insert string at cursorI
612  */
613 public int
614 el_insertstr(EditLine *el, const char *s)
615 {
616 	size_t len;
617 
618 	if ((len = strlen(s)) == 0)
619 		return (-1);
620 	if (el->el_line.lastchar + len >= el->el_line.limit) {
621 		if (!ch_enlargebufs(el, len))
622 			return (-1);
623 	}
624 
625 	c_insert(el, (int)len);
626 	while (*s)
627 		*el->el_line.cursor++ = *s++;
628 	return (0);
629 }
630 
631 
632 /* el_deletestr():
633  *	Delete num characters before the cursor
634  */
635 public void
636 el_deletestr(EditLine *el, int n)
637 {
638 	if (n <= 0)
639 		return;
640 
641 	if (el->el_line.cursor < &el->el_line.buffer[n])
642 		return;
643 
644 	c_delbefore(el, n);		/* delete before dot */
645 	el->el_line.cursor -= n;
646 	if (el->el_line.cursor < el->el_line.buffer)
647 		el->el_line.cursor = el->el_line.buffer;
648 }
649 
650 /* c_gets():
651  *	Get a string
652  */
653 protected int
654 c_gets(EditLine *el, char *buf, const char *prompt)
655 {
656 	char ch;
657 	int len;
658 	char *cp = el->el_line.buffer;
659 
660 	if (prompt) {
661 		len = strlen(prompt);
662 		memcpy(cp, prompt, len + 0u);
663 		cp += len;
664 	}
665 	len = 0;
666 
667 	for (;;) {
668 		el->el_line.cursor = cp;
669 		*cp = ' ';
670 		el->el_line.lastchar = cp + 1;
671 		re_refresh(el);
672 
673 		if (el_getc(el, &ch) != 1) {
674 			ed_end_of_file(el, 0);
675 			len = -1;
676 			break;
677 		}
678 
679 		switch (ch) {
680 
681 		case 0010:	/* Delete and backspace */
682 		case 0177:
683 			if (len <= 0) {
684 				len = -1;
685 				break;
686 			}
687 			cp--;
688 			continue;
689 
690 		case 0033:	/* ESC */
691 		case '\r':	/* Newline */
692 		case '\n':
693 			buf[len] = ch;
694 			break;
695 
696 		default:
697 			if (len >= EL_BUFSIZ - 16)
698 				term_beep(el);
699 			else {
700 				buf[len++] = ch;
701 				*cp++ = ch;
702 			}
703 			continue;
704 		}
705 		break;
706 	}
707 
708 	el->el_line.buffer[0] = '\0';
709 	el->el_line.lastchar = el->el_line.buffer;
710 	el->el_line.cursor = el->el_line.buffer;
711 	return len;
712 }
713 
714 
715 /* c_hpos():
716  *	Return the current horizontal position of the cursor
717  */
718 protected int
719 c_hpos(EditLine *el)
720 {
721 	char *ptr;
722 
723 	/*
724 	 * Find how many characters till the beginning of this line.
725 	 */
726 	if (el->el_line.cursor == el->el_line.buffer)
727 		return (0);
728 	else {
729 		for (ptr = el->el_line.cursor - 1;
730 		     ptr >= el->el_line.buffer && *ptr != '\n';
731 		     ptr--)
732 			continue;
733 		return (el->el_line.cursor - ptr - 1);
734 	}
735 }
736