1/*	$NetBSD: emacs.c,v 1.25 2011/07/29 15:16:33 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
38static char sccsid[] = "@(#)emacs.c	8.1 (Berkeley) 6/4/93";
39#else
40__RCSID("$NetBSD: emacs.c,v 1.25 2011/07/29 15:16:33 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 */
53protected el_action_t
54/*ARGSUSED*/
55em_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			terminal_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			terminal_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 */
89protected el_action_t
90/*ARGSUSED*/
91em_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, (int)(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 */
118protected el_action_t
119/*ARGSUSED*/
120em_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,
137	    (int)(el->el_chared.c_kill.last - el->el_chared.c_kill.buf));
138	/* copy the chars */
139	for (kp = el->el_chared.c_kill.buf; kp < el->el_chared.c_kill.last; kp++)
140		*cp++ = *kp;
141
142	/* if an arg, cursor at beginning else cursor at end */
143	if (el->el_state.argument == 1)
144		el->el_line.cursor = cp;
145
146	return CC_REFRESH;
147}
148
149
150/* em_kill_line():
151 *	Cut the entire line and save in cut buffer
152 *	[^U]
153 */
154protected el_action_t
155/*ARGSUSED*/
156em_kill_line(EditLine *el, Int c __attribute__((__unused__)))
157{
158	Char *kp, *cp;
159
160	cp = el->el_line.buffer;
161	kp = el->el_chared.c_kill.buf;
162	while (cp < el->el_line.lastchar)
163		*kp++ = *cp++;	/* copy it */
164	el->el_chared.c_kill.last = kp;
165				/* zap! -- delete all of it */
166	el->el_line.lastchar = el->el_line.buffer;
167	el->el_line.cursor = el->el_line.buffer;
168	return CC_REFRESH;
169}
170
171
172/* em_kill_region():
173 *	Cut area between mark and cursor and save in cut buffer
174 *	[^W]
175 */
176protected el_action_t
177/*ARGSUSED*/
178em_kill_region(EditLine *el, Int c __attribute__((__unused__)))
179{
180	Char *kp, *cp;
181
182	if (!el->el_chared.c_kill.mark)
183		return CC_ERROR;
184
185	if (el->el_chared.c_kill.mark > el->el_line.cursor) {
186		cp = el->el_line.cursor;
187		kp = el->el_chared.c_kill.buf;
188		while (cp < el->el_chared.c_kill.mark)
189			*kp++ = *cp++;	/* copy it */
190		el->el_chared.c_kill.last = kp;
191		c_delafter(el, (int)(cp - el->el_line.cursor));
192	} else {		/* mark is before cursor */
193		cp = el->el_chared.c_kill.mark;
194		kp = el->el_chared.c_kill.buf;
195		while (cp < el->el_line.cursor)
196			*kp++ = *cp++;	/* copy it */
197		el->el_chared.c_kill.last = kp;
198		c_delbefore(el, (int)(cp - el->el_chared.c_kill.mark));
199		el->el_line.cursor = el->el_chared.c_kill.mark;
200	}
201	return CC_REFRESH;
202}
203
204
205/* em_copy_region():
206 *	Copy area between mark and cursor to cut buffer
207 *	[M-W]
208 */
209protected el_action_t
210/*ARGSUSED*/
211em_copy_region(EditLine *el, Int c __attribute__((__unused__)))
212{
213	Char *kp, *cp;
214
215	if (!el->el_chared.c_kill.mark)
216		return CC_ERROR;
217
218	if (el->el_chared.c_kill.mark > el->el_line.cursor) {
219		cp = el->el_line.cursor;
220		kp = el->el_chared.c_kill.buf;
221		while (cp < el->el_chared.c_kill.mark)
222			*kp++ = *cp++;	/* copy it */
223		el->el_chared.c_kill.last = kp;
224	} else {
225		cp = el->el_chared.c_kill.mark;
226		kp = el->el_chared.c_kill.buf;
227		while (cp < el->el_line.cursor)
228			*kp++ = *cp++;	/* copy it */
229		el->el_chared.c_kill.last = kp;
230	}
231	return CC_NORM;
232}
233
234
235/* em_gosmacs_transpose():
236 *	Exchange the two characters before the cursor
237 *	Gosling emacs transpose chars [^T]
238 */
239protected el_action_t
240em_gosmacs_transpose(EditLine *el, Int c)
241{
242
243	if (el->el_line.cursor > &el->el_line.buffer[1]) {
244		/* must have at least two chars entered */
245		c = el->el_line.cursor[-2];
246		el->el_line.cursor[-2] = el->el_line.cursor[-1];
247		el->el_line.cursor[-1] = c;
248		return CC_REFRESH;
249	} else
250		return CC_ERROR;
251}
252
253
254/* em_next_word():
255 *	Move next to end of current word
256 *	[M-f]
257 */
258protected el_action_t
259/*ARGSUSED*/
260em_next_word(EditLine *el, Int c __attribute__((__unused__)))
261{
262	if (el->el_line.cursor == el->el_line.lastchar)
263		return CC_ERROR;
264
265	el->el_line.cursor = c__next_word(el->el_line.cursor,
266	    el->el_line.lastchar,
267	    el->el_state.argument,
268	    ce__isword);
269
270	if (el->el_map.type == MAP_VI)
271		if (el->el_chared.c_vcmd.action != NOP) {
272			cv_delfini(el);
273			return CC_REFRESH;
274		}
275	return CC_CURSOR;
276}
277
278
279/* em_upper_case():
280 *	Uppercase the characters from cursor to end of current word
281 *	[M-u]
282 */
283protected el_action_t
284/*ARGSUSED*/
285em_upper_case(EditLine *el, Int c __attribute__((__unused__)))
286{
287	Char *cp, *ep;
288
289	ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
290	    el->el_state.argument, ce__isword);
291
292	for (cp = el->el_line.cursor; cp < ep; cp++)
293		if (Islower(*cp))
294			*cp = Toupper(*cp);
295
296	el->el_line.cursor = ep;
297	if (el->el_line.cursor > el->el_line.lastchar)
298		el->el_line.cursor = el->el_line.lastchar;
299	return CC_REFRESH;
300}
301
302
303/* em_capitol_case():
304 *	Capitalize the characters from cursor to end of current word
305 *	[M-c]
306 */
307protected el_action_t
308/*ARGSUSED*/
309em_capitol_case(EditLine *el, Int c __attribute__((__unused__)))
310{
311	Char *cp, *ep;
312
313	ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
314	    el->el_state.argument, ce__isword);
315
316	for (cp = el->el_line.cursor; cp < ep; cp++) {
317		if (Isalpha(*cp)) {
318			if (Islower(*cp))
319				*cp = Toupper(*cp);
320			cp++;
321			break;
322		}
323	}
324	for (; cp < ep; cp++)
325		if (Isupper(*cp))
326			*cp = Tolower(*cp);
327
328	el->el_line.cursor = ep;
329	if (el->el_line.cursor > el->el_line.lastchar)
330		el->el_line.cursor = el->el_line.lastchar;
331	return CC_REFRESH;
332}
333
334
335/* em_lower_case():
336 *	Lowercase the characters from cursor to end of current word
337 *	[M-l]
338 */
339protected el_action_t
340/*ARGSUSED*/
341em_lower_case(EditLine *el, Int c __attribute__((__unused__)))
342{
343	Char *cp, *ep;
344
345	ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
346	    el->el_state.argument, ce__isword);
347
348	for (cp = el->el_line.cursor; cp < ep; cp++)
349		if (Isupper(*cp))
350			*cp = Tolower(*cp);
351
352	el->el_line.cursor = ep;
353	if (el->el_line.cursor > el->el_line.lastchar)
354		el->el_line.cursor = el->el_line.lastchar;
355	return CC_REFRESH;
356}
357
358
359/* em_set_mark():
360 *	Set the mark at cursor
361 *	[^@]
362 */
363protected el_action_t
364/*ARGSUSED*/
365em_set_mark(EditLine *el, Int c __attribute__((__unused__)))
366{
367
368	el->el_chared.c_kill.mark = el->el_line.cursor;
369	return CC_NORM;
370}
371
372
373/* em_exchange_mark():
374 *	Exchange the cursor and mark
375 *	[^X^X]
376 */
377protected el_action_t
378/*ARGSUSED*/
379em_exchange_mark(EditLine *el, Int c __attribute__((__unused__)))
380{
381	Char *cp;
382
383	cp = el->el_line.cursor;
384	el->el_line.cursor = el->el_chared.c_kill.mark;
385	el->el_chared.c_kill.mark = cp;
386	return CC_CURSOR;
387}
388
389
390/* em_universal_argument():
391 *	Universal argument (argument times 4)
392 *	[^U]
393 */
394protected el_action_t
395/*ARGSUSED*/
396em_universal_argument(EditLine *el, Int c __attribute__((__unused__)))
397{				/* multiply current argument by 4 */
398
399	if (el->el_state.argument > 1000000)
400		return CC_ERROR;
401	el->el_state.doingarg = 1;
402	el->el_state.argument *= 4;
403	return CC_ARGHACK;
404}
405
406
407/* em_meta_next():
408 *	Add 8th bit to next character typed
409 *	[<ESC>]
410 */
411protected el_action_t
412/*ARGSUSED*/
413em_meta_next(EditLine *el, Int c __attribute__((__unused__)))
414{
415
416	el->el_state.metanext = 1;
417	return CC_ARGHACK;
418}
419
420
421/* em_toggle_overwrite():
422 *	Switch from insert to overwrite mode or vice versa
423 */
424protected el_action_t
425/*ARGSUSED*/
426em_toggle_overwrite(EditLine *el, Int c __attribute__((__unused__)))
427{
428
429	el->el_state.inputmode = (el->el_state.inputmode == MODE_INSERT) ?
430	    MODE_REPLACE : MODE_INSERT;
431	return CC_NORM;
432}
433
434
435/* em_copy_prev_word():
436 *	Copy current word to cursor
437 */
438protected el_action_t
439/*ARGSUSED*/
440em_copy_prev_word(EditLine *el, Int c __attribute__((__unused__)))
441{
442	Char *cp, *oldc, *dp;
443
444	if (el->el_line.cursor == el->el_line.buffer)
445		return CC_ERROR;
446
447	oldc = el->el_line.cursor;
448	/* does a bounds check */
449	cp = c__prev_word(el->el_line.cursor, el->el_line.buffer,
450	    el->el_state.argument, ce__isword);
451
452	c_insert(el, (int)(oldc - cp));
453	for (dp = oldc; cp < oldc && dp < el->el_line.lastchar; cp++)
454		*dp++ = *cp;
455
456	el->el_line.cursor = dp;/* put cursor at end */
457
458	return CC_REFRESH;
459}
460
461
462/* em_inc_search_next():
463 *	Emacs incremental next search
464 */
465protected el_action_t
466/*ARGSUSED*/
467em_inc_search_next(EditLine *el, Int c __attribute__((__unused__)))
468{
469
470	el->el_search.patlen = 0;
471	return ce_inc_search(el, ED_SEARCH_NEXT_HISTORY);
472}
473
474
475/* em_inc_search_prev():
476 *	Emacs incremental reverse search
477 */
478protected el_action_t
479/*ARGSUSED*/
480em_inc_search_prev(EditLine *el, Int c __attribute__((__unused__)))
481{
482
483	el->el_search.patlen = 0;
484	return ce_inc_search(el, ED_SEARCH_PREV_HISTORY);
485}
486
487
488/* em_delete_prev_char():
489 *	Delete the character to the left of the cursor
490 *	[^?]
491 */
492protected el_action_t
493/*ARGSUSED*/
494em_delete_prev_char(EditLine *el, Int c __attribute__((__unused__)))
495{
496
497	if (el->el_line.cursor <= el->el_line.buffer)
498		return CC_ERROR;
499
500	if (el->el_state.doingarg)
501		c_delbefore(el, el->el_state.argument);
502	else
503		c_delbefore1(el);
504	el->el_line.cursor -= el->el_state.argument;
505	if (el->el_line.cursor < el->el_line.buffer)
506		el->el_line.cursor = el->el_line.buffer;
507	return CC_REFRESH;
508}
509