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