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