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