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