xref: /openbsd-src/lib/libedit/emacs.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: emacs.c,v 1.7 2003/11/25 20:12:38 otto 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 #if !defined(lint) && !defined(SCCSID)
38 #if 0
39 static char sccsid[] = "@(#)emacs.c	8.1 (Berkeley) 6/4/93";
40 #else
41 static const char rcsid[] = "$OpenBSD: emacs.c,v 1.7 2003/11/25 20:12:38 otto Exp $";
42 #endif
43 #endif /* not lint && not SCCSID */
44 
45 /*
46  * emacs.c: Emacs functions
47  */
48 #include "el.h"
49 
50 /* em_delete_or_list():
51  *	Delete character under cursor or list completions if at end of line
52  *	[^D]
53  */
54 protected el_action_t
55 /*ARGSUSED*/
56 em_delete_or_list(EditLine *el, int c __attribute__((__unused__)))
57 {
58 
59 	if (el->el_line.cursor == el->el_line.lastchar) {
60 					/* if I'm at the end */
61 		if (el->el_line.cursor == el->el_line.buffer) {
62 					/* and the beginning */
63 			term_overwrite(el, STReof, 4);	/* then do a EOF */
64 			term__flush();
65 			return (CC_EOF);
66 		} else {
67 			/*
68 			 * Here we could list completions, but it is an
69 			 * error right now
70 			 */
71 			term_beep(el);
72 			return (CC_ERROR);
73 		}
74 	} else {
75 		c_delafter(el, el->el_state.argument);	/* delete after dot */
76 		if (el->el_line.cursor > el->el_line.lastchar)
77 			el->el_line.cursor = el->el_line.lastchar;
78 				/* bounds check */
79 		return (CC_REFRESH);
80 	}
81 }
82 
83 
84 /* em_delete_next_word():
85  *	Cut from cursor to end of current word
86  *	[M-d]
87  */
88 protected el_action_t
89 /*ARGSUSED*/
90 em_delete_next_word(EditLine *el, int c __attribute__((__unused__)))
91 {
92 	char *cp, *p, *kp;
93 
94 	if (el->el_line.cursor == el->el_line.lastchar)
95 		return (CC_ERROR);
96 
97 	cp = c__next_word(el->el_line.cursor, el->el_line.lastchar,
98 	    el->el_state.argument, ce__isword);
99 
100 	for (p = el->el_line.cursor, kp = el->el_chared.c_kill.buf; p < cp; p++)
101 				/* save the text */
102 		*kp++ = *p;
103 	el->el_chared.c_kill.last = kp;
104 
105 	c_delafter(el, cp - el->el_line.cursor);	/* delete after dot */
106 	if (el->el_line.cursor > el->el_line.lastchar)
107 		el->el_line.cursor = el->el_line.lastchar;
108 				/* bounds check */
109 	return (CC_REFRESH);
110 }
111 
112 
113 /* em_yank():
114  *	Paste cut buffer at cursor position
115  *	[^Y]
116  */
117 protected el_action_t
118 /*ARGSUSED*/
119 em_yank(EditLine *el, int c __attribute__((__unused__)))
120 {
121 	char *kp, *cp;
122 
123 	if (el->el_chared.c_kill.last == el->el_chared.c_kill.buf)
124 		return (CC_NORM);
125 
126 	if (el->el_line.lastchar +
127 	    (el->el_chared.c_kill.last - el->el_chared.c_kill.buf) >=
128 	    el->el_line.limit)
129 		return (CC_ERROR);
130 
131 	el->el_chared.c_kill.mark = el->el_line.cursor;
132 	cp = el->el_line.cursor;
133 
134 	/* open the space, */
135 	c_insert(el, 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, int c __attribute__((__unused__)))
155 {
156 	char *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, int c __attribute__((__unused__)))
177 {
178 	char *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, 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, 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, int c __attribute__((__unused__)))
210 {
211 	char *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, int 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, int 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, int c __attribute__((__unused__)))
284 {
285 	char *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 (islower((unsigned char) *cp))
292 			*cp = toupper(*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, int c __attribute__((__unused__)))
308 {
309 	char *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 (isalpha((unsigned char) *cp)) {
316 			if (islower((unsigned char) *cp))
317 				*cp = toupper(*cp);
318 			cp++;
319 			break;
320 		}
321 	}
322 	for (; cp < ep; cp++)
323 		if (isupper((unsigned char) *cp))
324 			*cp = tolower(*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, int c __attribute__((__unused__)))
340 {
341 	char *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 (isupper((unsigned char) *cp))
348 			*cp = tolower(*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, int 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, int c __attribute__((__unused__)))
378 {
379 	char *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, int 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, int 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, int 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, int c __attribute__((__unused__)))
439 {
440 	char *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, 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, int 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, int c __attribute__((__unused__)))
479 {
480 
481 	el->el_search.patlen = 0;
482 	return (ce_inc_search(el, ED_SEARCH_PREV_HISTORY));
483 }
484