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