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