xref: /freebsd/contrib/libedit/emacs.c (revision 3c4ba5f55438f7afd4f4b0b56f88f2bb505fd6a6)
1 /*	$NetBSD: emacs.c,v 1.36 2016/05/09 21:46:56 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.36 2016/05/09 21:46:56 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 	cp = el->el_line.cursor;
138 
139 	/* open the space, */
140 	c_insert(el,
141 	    (int)(el->el_chared.c_kill.last - el->el_chared.c_kill.buf));
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 	oldc = el->el_line.cursor;
452 	/* does a bounds check */
453 	cp = c__prev_word(el->el_line.cursor, el->el_line.buffer,
454 	    el->el_state.argument, ce__isword);
455 
456 	c_insert(el, (int)(oldc - cp));
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