1/*	$NetBSD: emacs.c,v 1.36 2016/05/09 21:46: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
38static char sccsid[] = "@(#)emacs.c	8.1 (Berkeley) 6/4/93";
39#else
40__RCSID("$NetBSD: emacs.c,v 1.36 2016/05/09 21:46:56 christos Exp $");
41#endif
42#endif /* not lint && not SCCSID */
43
44/*
45 * emacs.c: Emacs functions
46 */
47#include <ctype.h>
48
49#include "el.h"
50#include "emacs.h"
51#include "fcns.h"
52
53/* em_delete_or_list():
54 *	Delete character under cursor or list completions if at end of line
55 *	[^D]
56 */
57libedit_private el_action_t
58/*ARGSUSED*/
59em_delete_or_list(EditLine *el, wint_t c)
60{
61
62	if (el->el_line.cursor == el->el_line.lastchar) {
63					/* if I'm at the end */
64		if (el->el_line.cursor == el->el_line.buffer) {
65					/* and the beginning */
66			terminal_writec(el, c);	/* then do an EOF */
67			return CC_EOF;
68		} else {
69			/*
70			 * Here we could list completions, but it is an
71			 * error right now
72			 */
73			terminal_beep(el);
74			return CC_ERROR;
75		}
76	} else {
77		if (el->el_state.doingarg)
78			c_delafter(el, el->el_state.argument);
79		else
80			c_delafter1(el);
81		if (el->el_line.cursor > el->el_line.lastchar)
82			el->el_line.cursor = el->el_line.lastchar;
83				/* bounds check */
84		return CC_REFRESH;
85	}
86}
87
88
89/* em_delete_next_word():
90 *	Cut from cursor to end of current word
91 *	[M-d]
92 */
93libedit_private el_action_t
94/*ARGSUSED*/
95em_delete_next_word(EditLine *el, wint_t c __attribute__((__unused__)))
96{
97	wchar_t *cp, *p, *kp;
98
99	if (el->el_line.cursor == el->el_line.lastchar)
100		return CC_ERROR;
101
102	cp = c__next_word(el->el_line.cursor, el->el_line.lastchar,
103	    el->el_state.argument, ce__isword);
104
105	for (p = el->el_line.cursor, kp = el->el_chared.c_kill.buf; p < cp; p++)
106				/* save the text */
107		*kp++ = *p;
108	el->el_chared.c_kill.last = kp;
109
110	c_delafter(el, (int)(cp - el->el_line.cursor));	/* delete after dot */
111	if (el->el_line.cursor > el->el_line.lastchar)
112		el->el_line.cursor = el->el_line.lastchar;
113				/* bounds check */
114	return CC_REFRESH;
115}
116
117
118/* em_yank():
119 *	Paste cut buffer at cursor position
120 *	[^Y]
121 */
122libedit_private el_action_t
123/*ARGSUSED*/
124em_yank(EditLine *el, wint_t c __attribute__((__unused__)))
125{
126	wchar_t *kp, *cp;
127
128	if (el->el_chared.c_kill.last == el->el_chared.c_kill.buf)
129		return CC_NORM;
130
131	if (el->el_line.lastchar +
132	    (el->el_chared.c_kill.last - el->el_chared.c_kill.buf) >=
133	    el->el_line.limit)
134		return CC_ERROR;
135
136	el->el_chared.c_kill.mark = el->el_line.cursor;
137	cp = el->el_line.cursor;
138
139	/* open the space, */
140	c_insert(el,
141	    (int)(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 */
158libedit_private el_action_t
159/*ARGSUSED*/
160em_kill_line(EditLine *el, wint_t c __attribute__((__unused__)))
161{
162	wchar_t *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 */
180libedit_private el_action_t
181/*ARGSUSED*/
182em_kill_region(EditLine *el, wint_t c __attribute__((__unused__)))
183{
184	wchar_t *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, (int)(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, (int)(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 */
213libedit_private el_action_t
214/*ARGSUSED*/
215em_copy_region(EditLine *el, wint_t c __attribute__((__unused__)))
216{
217	wchar_t *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_transpose():
240 *	Exchange the two characters before the cursor
241 *	Gosling emacs transpose chars [^T]
242 */
243libedit_private el_action_t
244em_gosmacs_transpose(EditLine *el, wint_t 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 */
262libedit_private el_action_t
263/*ARGSUSED*/
264em_next_word(EditLine *el, wint_t c __attribute__((__unused__)))
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 != NOP) {
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 */
287libedit_private el_action_t
288/*ARGSUSED*/
289em_upper_case(EditLine *el, wint_t c __attribute__((__unused__)))
290{
291	wchar_t *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 (iswlower(*cp))
298			*cp = towupper(*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 */
311libedit_private el_action_t
312/*ARGSUSED*/
313em_capitol_case(EditLine *el, wint_t c __attribute__((__unused__)))
314{
315	wchar_t *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 (iswalpha(*cp)) {
322			if (iswlower(*cp))
323				*cp = towupper(*cp);
324			cp++;
325			break;
326		}
327	}
328	for (; cp < ep; cp++)
329		if (iswupper(*cp))
330			*cp = towlower(*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 */
343libedit_private el_action_t
344/*ARGSUSED*/
345em_lower_case(EditLine *el, wint_t c __attribute__((__unused__)))
346{
347	wchar_t *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 (iswupper(*cp))
354			*cp = towlower(*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 */
367libedit_private el_action_t
368/*ARGSUSED*/
369em_set_mark(EditLine *el, wint_t c __attribute__((__unused__)))
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 */
381libedit_private el_action_t
382/*ARGSUSED*/
383em_exchange_mark(EditLine *el, wint_t c __attribute__((__unused__)))
384{
385	wchar_t *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 */
398libedit_private el_action_t
399/*ARGSUSED*/
400em_universal_argument(EditLine *el, wint_t c __attribute__((__unused__)))
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 */
415libedit_private el_action_t
416/*ARGSUSED*/
417em_meta_next(EditLine *el, wint_t c __attribute__((__unused__)))
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 */
428libedit_private el_action_t
429/*ARGSUSED*/
430em_toggle_overwrite(EditLine *el, wint_t c __attribute__((__unused__)))
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 */
442libedit_private el_action_t
443/*ARGSUSED*/
444em_copy_prev_word(EditLine *el, wint_t c __attribute__((__unused__)))
445{
446	wchar_t *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, (int)(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 */
469libedit_private el_action_t
470/*ARGSUSED*/
471em_inc_search_next(EditLine *el, wint_t c __attribute__((__unused__)))
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 */
482libedit_private el_action_t
483/*ARGSUSED*/
484em_inc_search_prev(EditLine *el, wint_t c __attribute__((__unused__)))
485{
486
487	el->el_search.patlen = 0;
488	return ce_inc_search(el, ED_SEARCH_PREV_HISTORY);
489}
490
491
492/* em_delete_prev_char():
493 *	Delete the character to the left of the cursor
494 *	[^?]
495 */
496libedit_private el_action_t
497/*ARGSUSED*/
498em_delete_prev_char(EditLine *el, wint_t c __attribute__((__unused__)))
499{
500
501	if (el->el_line.cursor <= el->el_line.buffer)
502		return CC_ERROR;
503
504	if (el->el_state.doingarg)
505		c_delbefore(el, el->el_state.argument);
506	else
507		c_delbefore1(el);
508	el->el_line.cursor -= el->el_state.argument;
509	if (el->el_line.cursor < el->el_line.buffer)
510		el->el_line.cursor = el->el_line.buffer;
511	return CC_REFRESH;
512}
513