chared.c revision 1574
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)
38static 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 */
52protected void
53cv_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 */
73protected void
74c_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 */
95protected void
96c_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 */
121protected void
122c_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 */
147protected int
148ce__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 */
158protected int
159cv__isword(p)
160    int p;
161{
162    return !isspace(p);
163}
164
165
166/* c__prev_word():
167 *	Find the previous word
168 */
169protected char *
170c__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 */
196protected char *
197c__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 */
217protected char *
218cv_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 */
250protected char *
251cv_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 */
290protected char *
291c__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 */
321protected void
322cv_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 */
376protected char *
377ce__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 */
399protected char *
400cv__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 */
425protected int
426ch_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 */
469protected void
470ch_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 */
504protected void
505ch_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 */
524public int
525el_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 */
546public void
547el_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 */
566protected int
567c_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 */
621protected int
622c_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