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