xref: /netbsd-src/lib/libedit/emacs.c (revision 1ca5c1b28139779176bd5c13ad7c5f25c0bcd5f8)
1 /*	$NetBSD: emacs.c,v 1.9 2001/01/10 07:45:41 jdolecek 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. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #if !defined(lint) && !defined(SCCSID)
41 #if 0
42 static char sccsid[] = "@(#)emacs.c	8.1 (Berkeley) 6/4/93";
43 #else
44 __RCSID("$NetBSD: emacs.c,v 1.9 2001/01/10 07:45:41 jdolecek Exp $");
45 #endif
46 #endif /* not lint && not SCCSID */
47 
48 /*
49  * emacs.c: Emacs functions
50  */
51 #include "sys.h"
52 #include "el.h"
53 
54 /* em_delete_or_list():
55  *	Delete character under cursor or list completions if at end of line
56  *	[^D]
57  */
58 protected el_action_t
59 /*ARGSUSED*/
60 em_delete_or_list(EditLine *el, int c)
61 {
62 
63 	if (el->el_line.cursor == el->el_line.lastchar) {
64 					/* if I'm at the end */
65 		if (el->el_line.cursor == el->el_line.buffer) {
66 					/* and the beginning */
67 			term_overwrite(el, STReof, 4);	/* then do a EOF */
68 			term__flush();
69 			return (CC_EOF);
70 		} else {
71 			/*
72 			 * Here we could list completions, but it is an
73 			 * error right now
74 			 */
75 			term_beep(el);
76 			return (CC_ERROR);
77 		}
78 	} else {
79 		c_delafter(el, el->el_state.argument);	/* delete after dot */
80 		if (el->el_line.cursor > el->el_line.lastchar)
81 			el->el_line.cursor = el->el_line.lastchar;
82 				/* bounds check */
83 		return (CC_REFRESH);
84 	}
85 }
86 
87 
88 /* em_delete_next_word():
89  *	Cut from cursor to end of current word
90  *	[M-d]
91  */
92 protected el_action_t
93 /*ARGSUSED*/
94 em_delete_next_word(EditLine *el, int c)
95 {
96 	char *cp, *p, *kp;
97 
98 	if (el->el_line.cursor == el->el_line.lastchar)
99 		return (CC_ERROR);
100 
101 	cp = c__next_word(el->el_line.cursor, el->el_line.lastchar,
102 	    el->el_state.argument, ce__isword);
103 
104 	for (p = el->el_line.cursor, kp = el->el_chared.c_kill.buf; p < cp; p++)
105 				/* save the text */
106 		*kp++ = *p;
107 	el->el_chared.c_kill.last = kp;
108 
109 	c_delafter(el, cp - el->el_line.cursor);	/* delete after dot */
110 	if (el->el_line.cursor > el->el_line.lastchar)
111 		el->el_line.cursor = el->el_line.lastchar;
112 				/* bounds check */
113 	return (CC_REFRESH);
114 }
115 
116 
117 /* em_yank():
118  *	Paste cut buffer at cursor position
119  *	[^Y]
120  */
121 protected el_action_t
122 /*ARGSUSED*/
123 em_yank(EditLine *el, int c)
124 {
125 	char *kp, *cp;
126 
127 	if (el->el_chared.c_kill.last == el->el_chared.c_kill.buf) {
128 		if (!ch_enlargebufs(el, 1))
129 			return (CC_ERROR);
130 	}
131 
132 	if (el->el_line.lastchar +
133 	    (el->el_chared.c_kill.last - el->el_chared.c_kill.buf) >=
134 	    el->el_line.limit)
135 		return (CC_ERROR);
136 
137 	el->el_chared.c_kill.mark = el->el_line.cursor;
138 	cp = el->el_line.cursor;
139 
140 	/* open the space, */
141 	c_insert(el, el->el_chared.c_kill.last - el->el_chared.c_kill.buf);
142 	/* copy the chars */
143 	for (kp = el->el_chared.c_kill.buf; kp < el->el_chared.c_kill.last; kp++)
144 		*cp++ = *kp;
145 
146 	/* if an arg, cursor at beginning else cursor at end */
147 	if (el->el_state.argument == 1)
148 		el->el_line.cursor = cp;
149 
150 	return (CC_REFRESH);
151 }
152 
153 
154 /* em_kill_line():
155  *	Cut the entire line and save in cut buffer
156  *	[^U]
157  */
158 protected el_action_t
159 /*ARGSUSED*/
160 em_kill_line(EditLine *el, int c)
161 {
162 	char *kp, *cp;
163 
164 	cp = el->el_line.buffer;
165 	kp = el->el_chared.c_kill.buf;
166 	while (cp < el->el_line.lastchar)
167 		*kp++ = *cp++;	/* copy it */
168 	el->el_chared.c_kill.last = kp;
169 				/* zap! -- delete all of it */
170 	el->el_line.lastchar = el->el_line.buffer;
171 	el->el_line.cursor = el->el_line.buffer;
172 	return (CC_REFRESH);
173 }
174 
175 
176 /* em_kill_region():
177  *	Cut area between mark and cursor and save in cut buffer
178  *	[^W]
179  */
180 protected el_action_t
181 /*ARGSUSED*/
182 em_kill_region(EditLine *el, int c)
183 {
184 	char *kp, *cp;
185 
186 	if (!el->el_chared.c_kill.mark)
187 		return (CC_ERROR);
188 
189 	if (el->el_chared.c_kill.mark > el->el_line.cursor) {
190 		cp = el->el_line.cursor;
191 		kp = el->el_chared.c_kill.buf;
192 		while (cp < el->el_chared.c_kill.mark)
193 			*kp++ = *cp++;	/* copy it */
194 		el->el_chared.c_kill.last = kp;
195 		c_delafter(el, cp - el->el_line.cursor);
196 	} else {		/* mark is before cursor */
197 		cp = el->el_chared.c_kill.mark;
198 		kp = el->el_chared.c_kill.buf;
199 		while (cp < el->el_line.cursor)
200 			*kp++ = *cp++;	/* copy it */
201 		el->el_chared.c_kill.last = kp;
202 		c_delbefore(el, cp - el->el_chared.c_kill.mark);
203 		el->el_line.cursor = el->el_chared.c_kill.mark;
204 	}
205 	return (CC_REFRESH);
206 }
207 
208 
209 /* em_copy_region():
210  *	Copy area between mark and cursor to cut buffer
211  *	[M-W]
212  */
213 protected el_action_t
214 /*ARGSUSED*/
215 em_copy_region(EditLine *el, int c)
216 {
217 	char *kp, *cp;
218 
219 	if (el->el_chared.c_kill.mark)
220 		return (CC_ERROR);
221 
222 	if (el->el_chared.c_kill.mark > el->el_line.cursor) {
223 		cp = el->el_line.cursor;
224 		kp = el->el_chared.c_kill.buf;
225 		while (cp < el->el_chared.c_kill.mark)
226 			*kp++ = *cp++;	/* copy it */
227 		el->el_chared.c_kill.last = kp;
228 	} else {
229 		cp = el->el_chared.c_kill.mark;
230 		kp = el->el_chared.c_kill.buf;
231 		while (cp < el->el_line.cursor)
232 			*kp++ = *cp++;	/* copy it */
233 		el->el_chared.c_kill.last = kp;
234 	}
235 	return (CC_NORM);
236 }
237 
238 
239 /* em_gosmacs_traspose():
240  *	Exchange the two characters before the cursor
241  *	Gosling emacs transpose chars [^T]
242  */
243 protected el_action_t
244 em_gosmacs_traspose(EditLine *el, int c)
245 {
246 
247 	if (el->el_line.cursor > &el->el_line.buffer[1]) {
248 		/* must have at least two chars entered */
249 		c = el->el_line.cursor[-2];
250 		el->el_line.cursor[-2] = el->el_line.cursor[-1];
251 		el->el_line.cursor[-1] = c;
252 		return (CC_REFRESH);
253 	} else
254 		return (CC_ERROR);
255 }
256 
257 
258 /* em_next_word():
259  *	Move next to end of current word
260  *	[M-f]
261  */
262 protected el_action_t
263 /*ARGSUSED*/
264 em_next_word(EditLine *el, int c)
265 {
266 	if (el->el_line.cursor == el->el_line.lastchar)
267 		return (CC_ERROR);
268 
269 	el->el_line.cursor = c__next_word(el->el_line.cursor,
270 	    el->el_line.lastchar,
271 	    el->el_state.argument,
272 	    ce__isword);
273 
274 	if (el->el_map.type == MAP_VI)
275 		if (el->el_chared.c_vcmd.action & DELETE) {
276 			cv_delfini(el);
277 			return (CC_REFRESH);
278 		}
279 	return (CC_CURSOR);
280 }
281 
282 
283 /* em_upper_case():
284  *	Uppercase the characters from cursor to end of current word
285  *	[M-u]
286  */
287 protected el_action_t
288 /*ARGSUSED*/
289 em_upper_case(EditLine *el, int c)
290 {
291 	char *cp, *ep;
292 
293 	ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
294 	    el->el_state.argument, ce__isword);
295 
296 	for (cp = el->el_line.cursor; cp < ep; cp++)
297 		if (islower((unsigned char) *cp))
298 			*cp = toupper(*cp);
299 
300 	el->el_line.cursor = ep;
301 	if (el->el_line.cursor > el->el_line.lastchar)
302 		el->el_line.cursor = el->el_line.lastchar;
303 	return (CC_REFRESH);
304 }
305 
306 
307 /* em_capitol_case():
308  *	Capitalize the characters from cursor to end of current word
309  *	[M-c]
310  */
311 protected el_action_t
312 /*ARGSUSED*/
313 em_capitol_case(EditLine *el, int c)
314 {
315 	char *cp, *ep;
316 
317 	ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
318 	    el->el_state.argument, ce__isword);
319 
320 	for (cp = el->el_line.cursor; cp < ep; cp++) {
321 		if (isalpha((unsigned char) *cp)) {
322 			if (islower((unsigned char) *cp))
323 				*cp = toupper(*cp);
324 			cp++;
325 			break;
326 		}
327 	}
328 	for (; cp < ep; cp++)
329 		if (isupper((unsigned char) *cp))
330 			*cp = tolower(*cp);
331 
332 	el->el_line.cursor = ep;
333 	if (el->el_line.cursor > el->el_line.lastchar)
334 		el->el_line.cursor = el->el_line.lastchar;
335 	return (CC_REFRESH);
336 }
337 
338 
339 /* em_lower_case():
340  *	Lowercase the characters from cursor to end of current word
341  *	[M-l]
342  */
343 protected el_action_t
344 /*ARGSUSED*/
345 em_lower_case(EditLine *el, int c)
346 {
347 	char *cp, *ep;
348 
349 	ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
350 	    el->el_state.argument, ce__isword);
351 
352 	for (cp = el->el_line.cursor; cp < ep; cp++)
353 		if (isupper((unsigned char) *cp))
354 			*cp = tolower(*cp);
355 
356 	el->el_line.cursor = ep;
357 	if (el->el_line.cursor > el->el_line.lastchar)
358 		el->el_line.cursor = el->el_line.lastchar;
359 	return (CC_REFRESH);
360 }
361 
362 
363 /* em_set_mark():
364  *	Set the mark at cursor
365  *	[^@]
366  */
367 protected el_action_t
368 /*ARGSUSED*/
369 em_set_mark(EditLine *el, int c)
370 {
371 
372 	el->el_chared.c_kill.mark = el->el_line.cursor;
373 	return (CC_NORM);
374 }
375 
376 
377 /* em_exchange_mark():
378  *	Exchange the cursor and mark
379  *	[^X^X]
380  */
381 protected el_action_t
382 /*ARGSUSED*/
383 em_exchange_mark(EditLine *el, int c)
384 {
385 	char *cp;
386 
387 	cp = el->el_line.cursor;
388 	el->el_line.cursor = el->el_chared.c_kill.mark;
389 	el->el_chared.c_kill.mark = cp;
390 	return (CC_CURSOR);
391 }
392 
393 
394 /* em_universal_argument():
395  *	Universal argument (argument times 4)
396  *	[^U]
397  */
398 protected el_action_t
399 /*ARGSUSED*/
400 em_universal_argument(EditLine *el, int c)
401 {				/* multiply current argument by 4 */
402 
403 	if (el->el_state.argument > 1000000)
404 		return (CC_ERROR);
405 	el->el_state.doingarg = 1;
406 	el->el_state.argument *= 4;
407 	return (CC_ARGHACK);
408 }
409 
410 
411 /* em_meta_next():
412  *	Add 8th bit to next character typed
413  *	[<ESC>]
414  */
415 protected el_action_t
416 /*ARGSUSED*/
417 em_meta_next(EditLine *el, int c)
418 {
419 
420 	el->el_state.metanext = 1;
421 	return (CC_ARGHACK);
422 }
423 
424 
425 /* em_toggle_overwrite():
426  *	Switch from insert to overwrite mode or vice versa
427  */
428 protected el_action_t
429 /*ARGSUSED*/
430 em_toggle_overwrite(EditLine *el, int c)
431 {
432 
433 	el->el_state.inputmode = (el->el_state.inputmode == MODE_INSERT) ?
434 	    MODE_REPLACE : MODE_INSERT;
435 	return (CC_NORM);
436 }
437 
438 
439 /* em_copy_prev_word():
440  *	Copy current word to cursor
441  */
442 protected el_action_t
443 /*ARGSUSED*/
444 em_copy_prev_word(EditLine *el, int c)
445 {
446 	char *cp, *oldc, *dp;
447 
448 	if (el->el_line.cursor == el->el_line.buffer)
449 		return (CC_ERROR);
450 
451 	oldc = el->el_line.cursor;
452 	/* does a bounds check */
453 	cp = c__prev_word(el->el_line.cursor, el->el_line.buffer,
454 	    el->el_state.argument, ce__isword);
455 
456 	c_insert(el, oldc - cp);
457 	for (dp = oldc; cp < oldc && dp < el->el_line.lastchar; cp++)
458 		*dp++ = *cp;
459 
460 	el->el_line.cursor = dp;/* put cursor at end */
461 
462 	return (CC_REFRESH);
463 }
464 
465 
466 /* em_inc_search_next():
467  *	Emacs incremental next search
468  */
469 protected el_action_t
470 /*ARGSUSED*/
471 em_inc_search_next(EditLine *el, int c)
472 {
473 
474 	el->el_search.patlen = 0;
475 	return (ce_inc_search(el, ED_SEARCH_NEXT_HISTORY));
476 }
477 
478 
479 /* em_inc_search_prev():
480  *	Emacs incremental reverse search
481  */
482 protected el_action_t
483 /*ARGSUSED*/
484 em_inc_search_prev(EditLine *el, int c)
485 {
486 
487 	el->el_search.patlen = 0;
488 	return (ce_inc_search(el, ED_SEARCH_PREV_HISTORY));
489 }
490