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