xref: /netbsd-src/lib/libedit/emacs.c (revision 73704c4ce4ee2a60eb617e693ce7e9f03902613e)
1 /*	$NetBSD: emacs.c,v 1.15 2003/08/07 16:44:31 agc 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.15 2003/08/07 16:44:31 agc 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 		if (!ch_enlargebufs(el, 1))
124 			return (CC_ERROR);
125 	}
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(*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(*cp);
319 			cp++;
320 			break;
321 		}
322 	}
323 	for (; cp < ep; cp++)
324 		if (isupper((unsigned char) *cp))
325 			*cp = tolower(*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(*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