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