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