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