Home | History | Annotate | Line # | Download | only in libedit
      1 /*	$NetBSD: emacs.c,v 1.38 2024/06/29 17:28:07 christos 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. Neither the name of the University nor the names of its contributors
     19  *    may be used to endorse or promote products derived from this software
     20  *    without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  * SUCH DAMAGE.
     33  */
     34 
     35 #include "config.h"
     36 #if !defined(lint) && !defined(SCCSID)
     37 #if 0
     38 static char sccsid[] = "@(#)emacs.c	8.1 (Berkeley) 6/4/93";
     39 #else
     40 __RCSID("$NetBSD: emacs.c,v 1.38 2024/06/29 17:28:07 christos Exp $");
     41 #endif
     42 #endif /* not lint && not SCCSID */
     43 
     44 /*
     45  * emacs.c: Emacs functions
     46  */
     47 #include <ctype.h>
     48 
     49 #include "el.h"
     50 #include "emacs.h"
     51 #include "fcns.h"
     52 
     53 /* em_delete_or_list():
     54  *	Delete character under cursor or list completions if at end of line
     55  *	[^D]
     56  */
     57 libedit_private el_action_t
     58 /*ARGSUSED*/
     59 em_delete_or_list(EditLine *el, wint_t c)
     60 {
     61 
     62 	if (el->el_line.cursor == el->el_line.lastchar) {
     63 					/* if I'm at the end */
     64 		if (el->el_line.cursor == el->el_line.buffer) {
     65 					/* and the beginning */
     66 			terminal_writec(el, c);	/* then do an EOF */
     67 			return CC_EOF;
     68 		} else {
     69 			/*
     70 			 * Here we could list completions, but it is an
     71 			 * error right now
     72 			 */
     73 			terminal_beep(el);
     74 			return CC_ERROR;
     75 		}
     76 	} else {
     77 		if (el->el_state.doingarg)
     78 			c_delafter(el, el->el_state.argument);
     79 		else
     80 			c_delafter1(el);
     81 		if (el->el_line.cursor > el->el_line.lastchar)
     82 			el->el_line.cursor = el->el_line.lastchar;
     83 				/* bounds check */
     84 		return CC_REFRESH;
     85 	}
     86 }
     87 
     88 
     89 /* em_delete_next_word():
     90  *	Cut from cursor to end of current word
     91  *	[M-d]
     92  */
     93 libedit_private el_action_t
     94 /*ARGSUSED*/
     95 em_delete_next_word(EditLine *el, wint_t c __attribute__((__unused__)))
     96 {
     97 	wchar_t *cp, *p, *kp;
     98 
     99 	if (el->el_line.cursor == el->el_line.lastchar)
    100 		return CC_ERROR;
    101 
    102 	cp = c__next_word(el->el_line.cursor, el->el_line.lastchar,
    103 	    el->el_state.argument, ce__isword);
    104 
    105 	for (p = el->el_line.cursor, kp = el->el_chared.c_kill.buf; p < cp; p++)
    106 				/* save the text */
    107 		*kp++ = *p;
    108 	el->el_chared.c_kill.last = kp;
    109 
    110 	c_delafter(el, (int)(cp - el->el_line.cursor));	/* delete after dot */
    111 	if (el->el_line.cursor > el->el_line.lastchar)
    112 		el->el_line.cursor = el->el_line.lastchar;
    113 				/* bounds check */
    114 	return CC_REFRESH;
    115 }
    116 
    117 
    118 /* em_yank():
    119  *	Paste cut buffer at cursor position
    120  *	[^Y]
    121  */
    122 libedit_private el_action_t
    123 /*ARGSUSED*/
    124 em_yank(EditLine *el, wint_t c __attribute__((__unused__)))
    125 {
    126 	wchar_t *kp, *cp;
    127 
    128 	if (el->el_chared.c_kill.last == el->el_chared.c_kill.buf)
    129 		return CC_NORM;
    130 
    131 	if (el->el_line.lastchar +
    132 	    (el->el_chared.c_kill.last - el->el_chared.c_kill.buf) >=
    133 	    el->el_line.limit)
    134 		return CC_ERROR;
    135 
    136 	el->el_chared.c_kill.mark = el->el_line.cursor;
    137 
    138 	/* open the space, */
    139 	c_insert(el,
    140 	    (int)(el->el_chared.c_kill.last - el->el_chared.c_kill.buf));
    141 	cp = el->el_line.cursor;
    142 	/* copy the chars */
    143 	for (kp = el->el_chared.c_kill.buf; kp < el->el_chared.c_kill.last; kp++)
    144 		*cp++ = *kp;
    145 
    146 	/* if an arg, cursor at beginning else cursor at end */
    147 	if (el->el_state.argument == 1)
    148 		el->el_line.cursor = cp;
    149 
    150 	return CC_REFRESH;
    151 }
    152 
    153 
    154 /* em_kill_line():
    155  *	Cut the entire line and save in cut buffer
    156  *	[^U]
    157  */
    158 libedit_private el_action_t
    159 /*ARGSUSED*/
    160 em_kill_line(EditLine *el, wint_t c __attribute__((__unused__)))
    161 {
    162 	wchar_t *kp, *cp;
    163 
    164 	cp = el->el_line.buffer;
    165 	kp = el->el_chared.c_kill.buf;
    166 	while (cp < el->el_line.lastchar)
    167 		*kp++ = *cp++;	/* copy it */
    168 	el->el_chared.c_kill.last = kp;
    169 				/* zap! -- delete all of it */
    170 	el->el_line.lastchar = el->el_line.buffer;
    171 	el->el_line.cursor = el->el_line.buffer;
    172 	return CC_REFRESH;
    173 }
    174 
    175 
    176 /* em_kill_region():
    177  *	Cut area between mark and cursor and save in cut buffer
    178  *	[^W]
    179  */
    180 libedit_private el_action_t
    181 /*ARGSUSED*/
    182 em_kill_region(EditLine *el, wint_t c __attribute__((__unused__)))
    183 {
    184 	wchar_t *kp, *cp;
    185 
    186 	if (!el->el_chared.c_kill.mark)
    187 		return CC_ERROR;
    188 
    189 	if (el->el_chared.c_kill.mark > el->el_line.cursor) {
    190 		cp = el->el_line.cursor;
    191 		kp = el->el_chared.c_kill.buf;
    192 		while (cp < el->el_chared.c_kill.mark)
    193 			*kp++ = *cp++;	/* copy it */
    194 		el->el_chared.c_kill.last = kp;
    195 		c_delafter(el, (int)(cp - el->el_line.cursor));
    196 	} else {		/* mark is before cursor */
    197 		cp = el->el_chared.c_kill.mark;
    198 		kp = el->el_chared.c_kill.buf;
    199 		while (cp < el->el_line.cursor)
    200 			*kp++ = *cp++;	/* copy it */
    201 		el->el_chared.c_kill.last = kp;
    202 		c_delbefore(el, (int)(cp - el->el_chared.c_kill.mark));
    203 		el->el_line.cursor = el->el_chared.c_kill.mark;
    204 	}
    205 	return CC_REFRESH;
    206 }
    207 
    208 
    209 /* em_copy_region():
    210  *	Copy area between mark and cursor to cut buffer
    211  *	[M-W]
    212  */
    213 libedit_private el_action_t
    214 /*ARGSUSED*/
    215 em_copy_region(EditLine *el, wint_t c __attribute__((__unused__)))
    216 {
    217 	wchar_t *kp, *cp;
    218 
    219 	if (!el->el_chared.c_kill.mark)
    220 		return CC_ERROR;
    221 
    222 	if (el->el_chared.c_kill.mark > el->el_line.cursor) {
    223 		cp = el->el_line.cursor;
    224 		kp = el->el_chared.c_kill.buf;
    225 		while (cp < el->el_chared.c_kill.mark)
    226 			*kp++ = *cp++;	/* copy it */
    227 		el->el_chared.c_kill.last = kp;
    228 	} else {
    229 		cp = el->el_chared.c_kill.mark;
    230 		kp = el->el_chared.c_kill.buf;
    231 		while (cp < el->el_line.cursor)
    232 			*kp++ = *cp++;	/* copy it */
    233 		el->el_chared.c_kill.last = kp;
    234 	}
    235 	return CC_NORM;
    236 }
    237 
    238 
    239 /* em_gosmacs_transpose():
    240  *	Exchange the two characters before the cursor
    241  *	Gosling emacs transpose chars [^T]
    242  */
    243 libedit_private el_action_t
    244 em_gosmacs_transpose(EditLine *el, wint_t c)
    245 {
    246 
    247 	if (el->el_line.cursor > &el->el_line.buffer[1]) {
    248 		/* must have at least two chars entered */
    249 		c = el->el_line.cursor[-2];
    250 		el->el_line.cursor[-2] = el->el_line.cursor[-1];
    251 		el->el_line.cursor[-1] = c;
    252 		return CC_REFRESH;
    253 	} else
    254 		return CC_ERROR;
    255 }
    256 
    257 
    258 /* em_next_word():
    259  *	Move next to end of current word
    260  *	[M-f]
    261  */
    262 libedit_private el_action_t
    263 /*ARGSUSED*/
    264 em_next_word(EditLine *el, wint_t c __attribute__((__unused__)))
    265 {
    266 	if (el->el_line.cursor == el->el_line.lastchar)
    267 		return CC_ERROR;
    268 
    269 	el->el_line.cursor = c__next_word(el->el_line.cursor,
    270 	    el->el_line.lastchar,
    271 	    el->el_state.argument,
    272 	    ce__isword);
    273 
    274 	if (el->el_map.type == MAP_VI)
    275 		if (el->el_chared.c_vcmd.action != NOP) {
    276 			cv_delfini(el);
    277 			return CC_REFRESH;
    278 		}
    279 	return CC_CURSOR;
    280 }
    281 
    282 
    283 /* em_upper_case():
    284  *	Uppercase the characters from cursor to end of current word
    285  *	[M-u]
    286  */
    287 libedit_private el_action_t
    288 /*ARGSUSED*/
    289 em_upper_case(EditLine *el, wint_t c __attribute__((__unused__)))
    290 {
    291 	wchar_t *cp, *ep;
    292 
    293 	ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
    294 	    el->el_state.argument, ce__isword);
    295 
    296 	for (cp = el->el_line.cursor; cp < ep; cp++)
    297 		if (iswlower(*cp))
    298 			*cp = towupper(*cp);
    299 
    300 	el->el_line.cursor = ep;
    301 	if (el->el_line.cursor > el->el_line.lastchar)
    302 		el->el_line.cursor = el->el_line.lastchar;
    303 	return CC_REFRESH;
    304 }
    305 
    306 
    307 /* em_capitol_case():
    308  *	Capitalize the characters from cursor to end of current word
    309  *	[M-c]
    310  */
    311 libedit_private el_action_t
    312 /*ARGSUSED*/
    313 em_capitol_case(EditLine *el, wint_t c __attribute__((__unused__)))
    314 {
    315 	wchar_t *cp, *ep;
    316 
    317 	ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
    318 	    el->el_state.argument, ce__isword);
    319 
    320 	for (cp = el->el_line.cursor; cp < ep; cp++) {
    321 		if (iswalpha(*cp)) {
    322 			if (iswlower(*cp))
    323 				*cp = towupper(*cp);
    324 			cp++;
    325 			break;
    326 		}
    327 	}
    328 	for (; cp < ep; cp++)
    329 		if (iswupper(*cp))
    330 			*cp = towlower(*cp);
    331 
    332 	el->el_line.cursor = ep;
    333 	if (el->el_line.cursor > el->el_line.lastchar)
    334 		el->el_line.cursor = el->el_line.lastchar;
    335 	return CC_REFRESH;
    336 }
    337 
    338 
    339 /* em_lower_case():
    340  *	Lowercase the characters from cursor to end of current word
    341  *	[M-l]
    342  */
    343 libedit_private el_action_t
    344 /*ARGSUSED*/
    345 em_lower_case(EditLine *el, wint_t c __attribute__((__unused__)))
    346 {
    347 	wchar_t *cp, *ep;
    348 
    349 	ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
    350 	    el->el_state.argument, ce__isword);
    351 
    352 	for (cp = el->el_line.cursor; cp < ep; cp++)
    353 		if (iswupper(*cp))
    354 			*cp = towlower(*cp);
    355 
    356 	el->el_line.cursor = ep;
    357 	if (el->el_line.cursor > el->el_line.lastchar)
    358 		el->el_line.cursor = el->el_line.lastchar;
    359 	return CC_REFRESH;
    360 }
    361 
    362 
    363 /* em_set_mark():
    364  *	Set the mark at cursor
    365  *	[^@]
    366  */
    367 libedit_private el_action_t
    368 /*ARGSUSED*/
    369 em_set_mark(EditLine *el, wint_t c __attribute__((__unused__)))
    370 {
    371 
    372 	el->el_chared.c_kill.mark = el->el_line.cursor;
    373 	return CC_NORM;
    374 }
    375 
    376 
    377 /* em_exchange_mark():
    378  *	Exchange the cursor and mark
    379  *	[^X^X]
    380  */
    381 libedit_private el_action_t
    382 /*ARGSUSED*/
    383 em_exchange_mark(EditLine *el, wint_t c __attribute__((__unused__)))
    384 {
    385 	wchar_t *cp;
    386 
    387 	cp = el->el_line.cursor;
    388 	el->el_line.cursor = el->el_chared.c_kill.mark;
    389 	el->el_chared.c_kill.mark = cp;
    390 	return CC_CURSOR;
    391 }
    392 
    393 
    394 /* em_universal_argument():
    395  *	Universal argument (argument times 4)
    396  *	[^U]
    397  */
    398 libedit_private el_action_t
    399 /*ARGSUSED*/
    400 em_universal_argument(EditLine *el, wint_t c __attribute__((__unused__)))
    401 {				/* multiply current argument by 4 */
    402 
    403 	if (el->el_state.argument > 1000000)
    404 		return CC_ERROR;
    405 	el->el_state.doingarg = 1;
    406 	el->el_state.argument *= 4;
    407 	return CC_ARGHACK;
    408 }
    409 
    410 
    411 /* em_meta_next():
    412  *	Add 8th bit to next character typed
    413  *	[<ESC>]
    414  */
    415 libedit_private el_action_t
    416 /*ARGSUSED*/
    417 em_meta_next(EditLine *el, wint_t c __attribute__((__unused__)))
    418 {
    419 
    420 	el->el_state.metanext = 1;
    421 	return CC_ARGHACK;
    422 }
    423 
    424 
    425 /* em_toggle_overwrite():
    426  *	Switch from insert to overwrite mode or vice versa
    427  */
    428 libedit_private el_action_t
    429 /*ARGSUSED*/
    430 em_toggle_overwrite(EditLine *el, wint_t c __attribute__((__unused__)))
    431 {
    432 
    433 	el->el_state.inputmode = (el->el_state.inputmode == MODE_INSERT) ?
    434 	    MODE_REPLACE : MODE_INSERT;
    435 	return CC_NORM;
    436 }
    437 
    438 
    439 /* em_copy_prev_word():
    440  *	Copy current word to cursor
    441  */
    442 libedit_private el_action_t
    443 /*ARGSUSED*/
    444 em_copy_prev_word(EditLine *el, wint_t c __attribute__((__unused__)))
    445 {
    446 	wchar_t *cp, *oldc, *dp;
    447 
    448 	if (el->el_line.cursor == el->el_line.buffer)
    449 		return CC_ERROR;
    450 
    451 	/* does a bounds check */
    452 	cp = c__prev_word(el->el_line.cursor, el->el_line.buffer,
    453 	    el->el_state.argument, ce__isword);
    454 
    455 	c_insert(el, (int)(el->el_line.cursor - cp));
    456 	oldc = el->el_line.cursor;
    457 	for (dp = oldc; cp < oldc && dp < el->el_line.lastchar; cp++)
    458 		*dp++ = *cp;
    459 
    460 	el->el_line.cursor = dp;/* put cursor at end */
    461 
    462 	return CC_REFRESH;
    463 }
    464 
    465 
    466 /* em_inc_search_next():
    467  *	Emacs incremental next search
    468  */
    469 libedit_private el_action_t
    470 /*ARGSUSED*/
    471 em_inc_search_next(EditLine *el, wint_t c __attribute__((__unused__)))
    472 {
    473 
    474 	el->el_search.patlen = 0;
    475 	return ce_inc_search(el, ED_SEARCH_NEXT_HISTORY);
    476 }
    477 
    478 
    479 /* em_inc_search_prev():
    480  *	Emacs incremental reverse search
    481  */
    482 libedit_private el_action_t
    483 /*ARGSUSED*/
    484 em_inc_search_prev(EditLine *el, wint_t c __attribute__((__unused__)))
    485 {
    486 
    487 	el->el_search.patlen = 0;
    488 	return ce_inc_search(el, ED_SEARCH_PREV_HISTORY);
    489 }
    490 
    491 
    492 /* em_delete_prev_char():
    493  *	Delete the character to the left of the cursor
    494  *	[^?]
    495  */
    496 libedit_private el_action_t
    497 /*ARGSUSED*/
    498 em_delete_prev_char(EditLine *el, wint_t c __attribute__((__unused__)))
    499 {
    500 
    501 	if (el->el_line.cursor <= el->el_line.buffer)
    502 		return CC_ERROR;
    503 
    504 	if (el->el_state.doingarg)
    505 		c_delbefore(el, el->el_state.argument);
    506 	else
    507 		c_delbefore1(el);
    508 	el->el_line.cursor -= el->el_state.argument;
    509 	if (el->el_line.cursor < el->el_line.buffer)
    510 		el->el_line.cursor = el->el_line.buffer;
    511 	return CC_REFRESH;
    512 }
    513