xref: /openbsd-src/lib/libedit/chared.c (revision db3296cf5c1dd9058ceecc3a29fe4aaa0bd26000)
1 /*	$OpenBSD: chared.c,v 1.6 2003/06/02 20:18:40 millert Exp $	*/
2 /*	$NetBSD: chared.c,v 1.2 1997/01/11 06:47:48 lukem 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 #if !defined(lint) && !defined(SCCSID)
37 #if 0
38 static char sccsid[] = "@(#)chared.c	8.1 (Berkeley) 6/4/93";
39 #else
40 static const char rcsid[] = "$OpenBSD: chared.c,v 1.6 2003/06/02 20:18:40 millert Exp $";
41 #endif
42 #endif /* not lint && not SCCSID */
43 
44 /*
45  * chared.c: Character editor utilities
46  */
47 #include "sys.h"
48 
49 #include <stdlib.h>
50 #include "el.h"
51 
52 /* cv_undo():
53  *	Handle state for the vi undo command
54  */
55 protected void
56 cv_undo(el, action, size, ptr)
57     EditLine *el;
58     int action, size;
59     char *ptr;
60 {
61     c_undo_t *vu = &el->el_chared.c_undo;
62     vu->action = action;
63     vu->ptr    = ptr;
64     vu->isize  = size;
65     (void)memcpy(vu->buf, vu->ptr, size);
66 #ifdef DEBUG_UNDO
67     (void)fprintf(el->el_errfile, "Undo buffer \"%s\" size = +%d -%d\n",
68 		   vu->ptr, vu->isize, vu->dsize);
69 #endif
70 }
71 
72 
73 /* c_insert():
74  *	Insert num characters
75  */
76 protected void
77 c_insert(el, num)
78     EditLine *el;
79     int num;
80 {
81     char *cp;
82 
83     if (el->el_line.lastchar + num >= el->el_line.limit)
84 	return;			/* can't go past end of buffer */
85 
86     if (el->el_line.cursor < el->el_line.lastchar) {
87 	/* if I must move chars */
88 	for (cp = el->el_line.lastchar; cp >= el->el_line.cursor; cp--)
89 	    cp[num] = *cp;
90     }
91     el->el_line.lastchar += num;
92 } /* end c_insert */
93 
94 
95 /* c_delafter():
96  *	Delete num characters after the cursor
97  */
98 protected void
99 c_delafter(el, num)
100     EditLine *el;
101     int num;
102 {
103 
104     if (el->el_line.cursor + num > el->el_line.lastchar)
105 	num = el->el_line.lastchar - el->el_line.cursor;
106 
107     if (num > 0) {
108 	char *cp;
109 
110 	if (el->el_map.current != el->el_map.emacs)
111 	    cv_undo(el, INSERT, num, el->el_line.cursor);
112 
113 	for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++)
114 	    *cp = cp[num];
115 
116 	el->el_line.lastchar -= num;
117     }
118 }
119 
120 
121 /* c_delbefore():
122  *	Delete num characters before the cursor
123  */
124 protected void
125 c_delbefore(el, num)
126     EditLine *el;
127     int num;
128 {
129 
130     if (el->el_line.cursor - num < el->el_line.buffer)
131 	num = el->el_line.cursor - el->el_line.buffer;
132 
133     if (num > 0) {
134 	char *cp;
135 
136 	if (el->el_map.current != el->el_map.emacs)
137 	    cv_undo(el, INSERT, num, el->el_line.cursor - num);
138 
139 	for (cp = el->el_line.cursor - num; cp <= el->el_line.lastchar; cp++)
140 	    *cp = cp[num];
141 
142 	el->el_line.lastchar -= num;
143     }
144 }
145 
146 
147 /* ce__isword():
148  *	Return if p is part of a word according to emacs
149  */
150 protected int
151 ce__isword(p)
152     int p;
153 {
154     return isalpha(p) || isdigit(p) || strchr("*?_-.[]~=", p) != NULL;
155 }
156 
157 
158 /* cv__isword():
159  *	Return if p is part of a word according to vi
160  */
161 protected int
162 cv__isword(p)
163     int p;
164 {
165     return !isspace(p);
166 }
167 
168 
169 /* c__prev_word():
170  *	Find the previous word
171  */
172 protected char *
173 c__prev_word(p, low, n, wtest)
174     register char *p, *low;
175     register int n;
176     int (*wtest)(int);
177 {
178     p--;
179 
180     while (n--) {
181 	while ((p >= low) && !(*wtest)((unsigned char) *p))
182 	    p--;
183 	while ((p >= low) && (*wtest)((unsigned char) *p))
184 	    p--;
185     }
186 
187     /* cp now points to one character before the word */
188     p++;
189     if (p < low)
190 	p = low;
191     /* cp now points where we want it */
192     return p;
193 }
194 
195 
196 /* c__next_word():
197  *	Find the next word
198  */
199 protected char *
200 c__next_word(p, high, n, wtest)
201     register char *p, *high;
202     register int n;
203     int (*wtest)(int);
204 {
205     while (n--) {
206 	while ((p < high) && !(*wtest)((unsigned char) *p))
207 	    p++;
208 	while ((p < high) && (*wtest)((unsigned char) *p))
209 	    p++;
210     }
211     if (p > high)
212 	p = high;
213     /* p now points where we want it */
214     return p;
215 }
216 
217 /* cv_next_word():
218  *	Find the next word vi style
219  */
220 protected char *
221 cv_next_word(el, p, high, n, wtest)
222     EditLine *el;
223     register char *p, *high;
224     register int n;
225     int (*wtest)(int);
226 {
227     int test;
228 
229     while (n--) {
230     	test = (*wtest)((unsigned char) *p);
231 	while ((p < high) && (*wtest)((unsigned char) *p) == test)
232 	    p++;
233 	/*
234 	 * vi historically deletes with cw only the word preserving the
235 	 * trailing whitespace! This is not what 'w' does..
236 	 */
237 	if (el->el_chared.c_vcmd.action != (DELETE|INSERT))
238 	    while ((p < high) && isspace((unsigned char) *p))
239 		p++;
240     }
241 
242     /* p now points where we want it */
243     if (p > high)
244 	return high;
245     else
246 	return p;
247 }
248 
249 
250 /* cv_prev_word():
251  *	Find the previous word vi style
252  */
253 protected char *
254 cv_prev_word(el, p, low, n, wtest)
255     EditLine *el;
256     register char *p, *low;
257     register int n;
258     int (*wtest)(int);
259 {
260     int test;
261 
262     while (n--) {
263 	p--;
264 	/*
265 	 * vi historically deletes with cb only the word preserving the
266 	 * leading whitespace! This is not what 'b' does..
267 	 */
268 	if (el->el_chared.c_vcmd.action != (DELETE|INSERT))
269 	    while ((p > low) && isspace((unsigned char) *p))
270 		p--;
271 	test = (*wtest)((unsigned char) *p);
272 	while ((p >= low) && (*wtest)((unsigned char) *p) == test)
273 	    p--;
274 	p++;
275 	while (isspace((unsigned char) *p))
276 		p++;
277     }
278 
279     /* p now points where we want it */
280     if (p < low)
281 	return low;
282     else
283 	return p;
284 }
285 
286 
287 #ifdef notdef
288 /* c__number():
289  *	Ignore character p points to, return number appearing after that.
290  * 	A '$' by itself means a big number; "$-" is for negative; '^' means 1.
291  * 	Return p pointing to last char used.
292  */
293 protected char *
294 c__number(p, num, dval)
295     char *p;	/* character position */
296     int *num;	/* Return value	*/
297     int dval;	/* dval is the number to subtract from like $-3 */
298 {
299     register int i;
300     register int sign = 1;
301 
302     if (*++p == '^') {
303 	*num = 1;
304 	return p;
305     }
306     if (*p == '$') {
307 	if (*++p != '-') {
308 	    *num = 0x7fffffff;	/* Handle $ */
309 	    return --p;
310 	}
311 	sign = -1;		/* Handle $- */
312 	++p;
313     }
314     for (i = 0; isdigit((unsigned char) *p); i = 10 * i + *p++ - '0')
315 	continue;
316     *num = (sign < 0 ? dval - i : i);
317     return --p;
318 }
319 #endif
320 
321 /* cv_delfini():
322  *	Finish vi delete action
323  */
324 protected void
325 cv_delfini(el)
326     EditLine *el;
327 {
328     register int size;
329     int oaction;
330 
331     if (el->el_chared.c_vcmd.action & INSERT)
332 	el->el_map.current = el->el_map.key;
333 
334     oaction = el->el_chared.c_vcmd.action;
335     el->el_chared.c_vcmd.action = NOP;
336 
337     if (el->el_chared.c_vcmd.pos == 0)
338 	return;
339 
340 
341     if (el->el_line.cursor > el->el_chared.c_vcmd.pos) {
342 	size = (int) (el->el_line.cursor - el->el_chared.c_vcmd.pos);
343 	c_delbefore(el, size);
344 	el->el_line.cursor = el->el_chared.c_vcmd.pos;
345 	re_refresh_cursor(el);
346     }
347     else if (el->el_line.cursor < el->el_chared.c_vcmd.pos) {
348 	size = (int)(el->el_chared.c_vcmd.pos - el->el_line.cursor);
349 	c_delafter(el, size);
350     }
351     else {
352 	size = 1;
353 	c_delafter(el, size);
354     }
355     switch (oaction) {
356     case DELETE|INSERT:
357 	el->el_chared.c_undo.action = DELETE|INSERT;
358 	break;
359     case DELETE:
360 	el->el_chared.c_undo.action = INSERT;
361 	break;
362     case NOP:
363     case INSERT:
364     default:
365 	abort();
366 	break;
367     }
368 
369 
370     el->el_chared.c_undo.ptr = el->el_line.cursor;
371     el->el_chared.c_undo.dsize = size;
372 }
373 
374 
375 #ifdef notdef
376 /* ce__endword():
377  *	Go to the end of this word according to emacs
378  */
379 protected char *
380 ce__endword(p, high, n)
381     char *p, *high;
382     int n;
383 {
384     p++;
385 
386     while (n--) {
387 	while ((p < high) && isspace((unsigned char) *p))
388 	    p++;
389 	while ((p < high) && !isspace((unsigned char) *p))
390 	    p++;
391     }
392 
393     p--;
394     return p;
395 }
396 #endif
397 
398 
399 /* cv__endword():
400  *	Go to the end of this word according to vi
401  */
402 protected char *
403 cv__endword(p, high, n)
404     char *p, *high;
405     int n;
406 {
407     p++;
408 
409     while (n--) {
410 	while ((p < high) && isspace((unsigned char) *p))
411 	    p++;
412 
413 	if (isalnum((unsigned char) *p))
414 	    while ((p < high) && isalnum((unsigned char) *p))
415 		p++;
416 	else
417 	    while ((p < high) && !(isspace((unsigned char) *p) ||
418 				   isalnum((unsigned char) *p)))
419 		p++;
420     }
421     p--;
422     return p;
423 }
424 
425 /* ch_init():
426  *	Initialize the character editor
427  */
428 protected int
429 ch_init(el)
430     EditLine *el;
431 {
432     el->el_line.buffer              = (char *)  el_malloc(EL_BUFSIZ);
433     (void)memset(el->el_line.buffer, 0, EL_BUFSIZ);
434     el->el_line.cursor              = el->el_line.buffer;
435     el->el_line.lastchar            = el->el_line.buffer;
436     el->el_line.limit  		    = &el->el_line.buffer[EL_BUFSIZ - 2];
437 
438     el->el_chared.c_undo.buf        = (char *)  el_malloc(EL_BUFSIZ);
439     (void)memset(el->el_chared.c_undo.buf, 0, EL_BUFSIZ);
440     el->el_chared.c_undo.action     = NOP;
441     el->el_chared.c_undo.isize      = 0;
442     el->el_chared.c_undo.dsize      = 0;
443     el->el_chared.c_undo.ptr        = el->el_line.buffer;
444 
445     el->el_chared.c_vcmd.action     = NOP;
446     el->el_chared.c_vcmd.pos        = el->el_line.buffer;
447     el->el_chared.c_vcmd.ins        = el->el_line.buffer;
448 
449     el->el_chared.c_kill.buf        = (char *)  el_malloc(EL_BUFSIZ);
450     (void)memset(el->el_chared.c_kill.buf, 0, EL_BUFSIZ);
451     el->el_chared.c_kill.mark       = el->el_line.buffer;
452     el->el_chared.c_kill.last       = el->el_chared.c_kill.buf;
453 
454     el->el_map.current              = el->el_map.key;
455 
456     el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */
457     el->el_state.doingarg  = 0;
458     el->el_state.metanext  = 0;
459     el->el_state.argument  = 1;
460     el->el_state.lastcmd   = ED_UNASSIGNED;
461 
462     el->el_chared.c_macro.nline     = NULL;
463     el->el_chared.c_macro.level     = -1;
464     el->el_chared.c_macro.macro     = (char **) el_malloc(EL_MAXMACRO *
465 						          sizeof(char *));
466     return 0;
467 }
468 
469 /* ch_reset():
470  *	Reset the character editor
471  */
472 protected void
473 ch_reset(el)
474     EditLine *el;
475 {
476     el->el_line.cursor              = el->el_line.buffer;
477     el->el_line.lastchar            = el->el_line.buffer;
478 
479     el->el_chared.c_undo.action     = NOP;
480     el->el_chared.c_undo.isize      = 0;
481     el->el_chared.c_undo.dsize      = 0;
482     el->el_chared.c_undo.ptr        = el->el_line.buffer;
483 
484     el->el_chared.c_vcmd.action     = NOP;
485     el->el_chared.c_vcmd.pos        = el->el_line.buffer;
486     el->el_chared.c_vcmd.ins        = el->el_line.buffer;
487 
488     el->el_chared.c_kill.mark       = el->el_line.buffer;
489 
490     el->el_map.current              = el->el_map.key;
491 
492     el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */
493     el->el_state.doingarg  = 0;
494     el->el_state.metanext  = 0;
495     el->el_state.argument  = 1;
496     el->el_state.lastcmd   = ED_UNASSIGNED;
497 
498     el->el_chared.c_macro.level     = -1;
499 
500     el->el_history.eventno = 0;
501 }
502 
503 
504 /* ch_end():
505  *	Free the data structures used by the editor
506  */
507 protected void
508 ch_end(el)
509     EditLine *el;
510 {
511     el_free((ptr_t) el->el_line.buffer);
512     el->el_line.buffer = NULL;
513     el->el_line.limit = NULL;
514     el_free((ptr_t) el->el_chared.c_undo.buf);
515     el->el_chared.c_undo.buf = NULL;
516     el_free((ptr_t) el->el_chared.c_kill.buf);
517     el->el_chared.c_kill.buf = NULL;
518     el_free((ptr_t) el->el_chared.c_macro.macro);
519     el->el_chared.c_macro.macro = NULL;
520     ch_reset(el);
521 }
522 
523 
524 /* el_insertstr():
525  *	Insert string at cursorI
526  */
527 public int
528 el_insertstr(el, s)
529     EditLine *el;
530     char   *s;
531 {
532     int len;
533 
534     if ((len = strlen(s)) == 0)
535 	return -1;
536     if (el->el_line.lastchar + len >= el->el_line.limit)
537 	return -1;
538 
539     c_insert(el, len);
540     while (*s)
541 	*el->el_line.cursor++ = *s++;
542     return 0;
543 }
544 
545 
546 /* el_deletestr():
547  *	Delete num characters before the cursor
548  */
549 public void
550 el_deletestr(el, n)
551     EditLine *el;
552     int     n;
553 {
554     if (n <= 0)
555 	return;
556 
557     if (el->el_line.cursor < &el->el_line.buffer[n])
558 	return;
559 
560     c_delbefore(el, n);		/* delete before dot */
561     el->el_line.cursor -= n;
562     if (el->el_line.cursor < el->el_line.buffer)
563 	el->el_line.cursor = el->el_line.buffer;
564 }
565 
566 /* c_gets():
567  *	Get a string
568  */
569 protected int
570 c_gets(el, buf)
571     EditLine *el;
572     char *buf;
573 {
574     char ch;
575     int len = 0;
576 
577     for (ch = 0; ch == 0;) {
578 	if (el_getc(el, &ch) != 1)
579 	    return ed_end_of_file(el, 0);
580 	switch (ch) {
581 	case 0010:	/* Delete and backspace */
582 	case 0177:
583 	    if (len > 1) {
584 		*el->el_line.cursor-- = '\0';
585 		el->el_line.lastchar = el->el_line.cursor;
586 		buf[len--] = '\0';
587 	    }
588 	    else {
589 		el->el_line.buffer[0] = '\0';
590 		el->el_line.lastchar = el->el_line.buffer;
591 		el->el_line.cursor = el->el_line.buffer;
592 		return CC_REFRESH;
593 	    }
594 	    re_refresh(el);
595 	    ch = 0;
596 	    break;
597 
598 	case 0033:	/* ESC */
599 	case '\r':	/* Newline */
600 	case '\n':
601 	    break;
602 
603 	default:
604 	    if (len >= EL_BUFSIZ)
605 		term_beep(el);
606 	    else {
607 		buf[len++] = ch;
608 		*el->el_line.cursor++ = ch;
609 		el->el_line.lastchar = el->el_line.cursor;
610 	    }
611 	    re_refresh(el);
612 	    ch = 0;
613 	    break;
614 	}
615     }
616     buf[len] = ch;
617     return len;
618 }
619 
620 
621 /* c_hpos():
622  *	Return the current horizontal position of the cursor
623  */
624 protected int
625 c_hpos(el)
626     EditLine *el;
627 {
628     char *ptr;
629 
630     /*
631      * Find how many characters till the beginning of this line.
632      */
633     if (el->el_line.cursor == el->el_line.buffer)
634 	return 0;
635     else {
636 	for (ptr = el->el_line.cursor - 1;
637 	     ptr >= el->el_line.buffer && *ptr != '\n';
638 	     ptr--)
639 	    continue;
640 	return el->el_line.cursor - ptr - 1;
641     }
642 }
643