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