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