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