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