xref: /openbsd/lib/libedit/common.c (revision 3cab2bb3)
1 /*	$OpenBSD: common.c,v 1.22 2016/05/22 23:09:56 schwarze Exp $	*/
2 /*	$NetBSD: common.c,v 1.24 2009/12/30 22:37:40 christos 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 "config.h"
37 
38 /*
39  * common.c: Common Editor functions
40  */
41 #include <ctype.h>
42 #include <string.h>
43 
44 #include "el.h"
45 #include "common.h"
46 #include "fcns.h"
47 #include "parse.h"
48 #include "vi.h"
49 
50 /* ed_end_of_file():
51  *	Indicate end of file
52  *	[^D]
53  */
54 protected el_action_t
55 /*ARGSUSED*/
56 ed_end_of_file(EditLine *el, wint_t c __attribute__((__unused__)))
57 {
58 
59 	re_goto_bottom(el);
60 	*el->el_line.lastchar = '\0';
61 	return CC_EOF;
62 }
63 
64 
65 /* ed_insert():
66  *	Add character to the line
67  *	Insert a character [bound to all insert keys]
68  */
69 protected el_action_t
70 ed_insert(EditLine *el, wint_t c)
71 {
72 	int count = el->el_state.argument;
73 
74 	if (c == '\0')
75 		return CC_ERROR;
76 
77 	if (el->el_line.lastchar + el->el_state.argument >=
78 	    el->el_line.limit) {
79 		/* end of buffer space, try to allocate more */
80 		if (!ch_enlargebufs(el, (size_t) count))
81 			return CC_ERROR;	/* error allocating more */
82 	}
83 
84 	if (count == 1) {
85 		if (el->el_state.inputmode == MODE_INSERT
86 		    || el->el_line.cursor >= el->el_line.lastchar)
87 			c_insert(el, 1);
88 
89 		*el->el_line.cursor++ = c;
90 		re_fastaddc(el);		/* fast refresh for one char. */
91 	} else {
92 		if (el->el_state.inputmode != MODE_REPLACE_1)
93 			c_insert(el, el->el_state.argument);
94 
95 		while (count-- && el->el_line.cursor < el->el_line.lastchar)
96 			*el->el_line.cursor++ = c;
97 		re_refresh(el);
98 	}
99 
100 	if (el->el_state.inputmode == MODE_REPLACE_1)
101 		return vi_command_mode(el, 0);
102 
103 	return CC_NORM;
104 }
105 
106 
107 /* ed_delete_prev_word():
108  *	Delete from beginning of current word to cursor
109  *	[M-^?] [^W]
110  */
111 protected el_action_t
112 /*ARGSUSED*/
113 ed_delete_prev_word(EditLine *el, wint_t c __attribute__((__unused__)))
114 {
115 	wchar_t *cp, *p, *kp;
116 
117 	if (el->el_line.cursor == el->el_line.buffer)
118 		return CC_ERROR;
119 
120 	cp = c__prev_word(el->el_line.cursor, el->el_line.buffer,
121 	    el->el_state.argument, ce__isword);
122 
123 	for (p = cp, kp = el->el_chared.c_kill.buf; p < el->el_line.cursor; p++)
124 		*kp++ = *p;
125 	el->el_chared.c_kill.last = kp;
126 
127 	c_delbefore(el, (int)(el->el_line.cursor - cp));/* delete before dot */
128 	el->el_line.cursor = cp;
129 	if (el->el_line.cursor < el->el_line.buffer)
130 		el->el_line.cursor = el->el_line.buffer; /* bounds check */
131 	return CC_REFRESH;
132 }
133 
134 
135 /* ed_delete_next_char():
136  *	Delete character under cursor
137  *	[^D] [x]
138  */
139 protected el_action_t
140 /*ARGSUSED*/
141 ed_delete_next_char(EditLine *el, wint_t c)
142 {
143 #ifdef notdef			/* XXX */
144 #define	EL	el->el_line
145 	(void) fprintf(el->el_errfile,
146 	    "\nD(b: %p(%ls)  c: %p(%ls) last: %p(%ls) limit: %p(%ls)\n",
147 	    EL.buffer, EL.buffer, EL.cursor, EL.cursor, EL.lastchar,
148 	    EL.lastchar, EL.limit, EL.limit);
149 #endif
150 	if (el->el_line.cursor == el->el_line.lastchar) {
151 			/* if I'm at the end */
152 		if (el->el_map.type == MAP_VI) {
153 			if (el->el_line.cursor == el->el_line.buffer) {
154 				/* if I'm also at the beginning */
155 #ifdef KSHVI
156 				return CC_ERROR;
157 #else
158 				/* then do an EOF */
159 				terminal_writec(el, c);
160 				return CC_EOF;
161 #endif
162 			} else {
163 #ifdef KSHVI
164 				el->el_line.cursor--;
165 #else
166 				return CC_ERROR;
167 #endif
168 			}
169 		} else {
170 			if (el->el_line.cursor != el->el_line.buffer)
171 				el->el_line.cursor--;
172 			else
173 				return CC_ERROR;
174 		}
175 	}
176 	c_delafter(el, el->el_state.argument);	/* delete after dot */
177 	if (el->el_line.cursor >= el->el_line.lastchar &&
178 	    el->el_line.cursor > el->el_line.buffer)
179 			/* bounds check */
180 		el->el_line.cursor = el->el_line.lastchar - 1;
181 	return CC_REFRESH;
182 }
183 
184 
185 /* ed_kill_line():
186  *	Cut to the end of line
187  *	[^K] [^K]
188  */
189 protected el_action_t
190 /*ARGSUSED*/
191 ed_kill_line(EditLine *el, wint_t c __attribute__((__unused__)))
192 {
193 	wchar_t *kp, *cp;
194 
195 	cp = el->el_line.cursor;
196 	kp = el->el_chared.c_kill.buf;
197 	while (cp < el->el_line.lastchar)
198 		*kp++ = *cp++;	/* copy it */
199 	el->el_chared.c_kill.last = kp;
200 			/* zap! -- delete to end */
201 	el->el_line.lastchar = el->el_line.cursor;
202 	return CC_REFRESH;
203 }
204 
205 
206 /* ed_move_to_end():
207  *	Move cursor to the end of line
208  *	[^E] [^E]
209  */
210 protected el_action_t
211 /*ARGSUSED*/
212 ed_move_to_end(EditLine *el, wint_t c __attribute__((__unused__)))
213 {
214 
215 	el->el_line.cursor = el->el_line.lastchar;
216 	if (el->el_map.type == MAP_VI) {
217 		if (el->el_chared.c_vcmd.action != NOP) {
218 			cv_delfini(el);
219 			return CC_REFRESH;
220 		}
221 #ifdef VI_MOVE
222 		el->el_line.cursor--;
223 #endif
224 	}
225 	return CC_CURSOR;
226 }
227 
228 
229 /* ed_move_to_beg():
230  *	Move cursor to the beginning of line
231  *	[^A] [^A]
232  */
233 protected el_action_t
234 /*ARGSUSED*/
235 ed_move_to_beg(EditLine *el, wint_t c __attribute__((__unused__)))
236 {
237 
238 	el->el_line.cursor = el->el_line.buffer;
239 
240 	if (el->el_map.type == MAP_VI) {
241 			/* We want FIRST non space character */
242 		while (iswspace(*el->el_line.cursor))
243 			el->el_line.cursor++;
244 		if (el->el_chared.c_vcmd.action != NOP) {
245 			cv_delfini(el);
246 			return CC_REFRESH;
247 		}
248 	}
249 	return CC_CURSOR;
250 }
251 
252 
253 /* ed_transpose_chars():
254  *	Exchange the character to the left of the cursor with the one under it
255  *	[^T] [^T]
256  */
257 protected el_action_t
258 ed_transpose_chars(EditLine *el, wint_t c)
259 {
260 
261 	if (el->el_line.cursor < el->el_line.lastchar) {
262 		if (el->el_line.lastchar <= &el->el_line.buffer[1])
263 			return CC_ERROR;
264 		else
265 			el->el_line.cursor++;
266 	}
267 	if (el->el_line.cursor > &el->el_line.buffer[1]) {
268 		/* must have at least two chars entered */
269 		c = el->el_line.cursor[-2];
270 		el->el_line.cursor[-2] = el->el_line.cursor[-1];
271 		el->el_line.cursor[-1] = c;
272 		return CC_REFRESH;
273 	} else
274 		return CC_ERROR;
275 }
276 
277 
278 /* ed_next_char():
279  *	Move to the right one character
280  *	[^F] [^F]
281  */
282 protected el_action_t
283 /*ARGSUSED*/
284 ed_next_char(EditLine *el, wint_t c __attribute__((__unused__)))
285 {
286 	wchar_t *lim = el->el_line.lastchar;
287 
288 	if (el->el_line.cursor >= lim ||
289 	    (el->el_line.cursor == lim - 1 &&
290 	    el->el_map.type == MAP_VI &&
291 	    el->el_chared.c_vcmd.action == NOP))
292 		return CC_ERROR;
293 
294 	el->el_line.cursor += el->el_state.argument;
295 	if (el->el_line.cursor > lim)
296 		el->el_line.cursor = lim;
297 
298 	if (el->el_map.type == MAP_VI)
299 		if (el->el_chared.c_vcmd.action != NOP) {
300 			cv_delfini(el);
301 			return CC_REFRESH;
302 		}
303 	return CC_CURSOR;
304 }
305 
306 
307 /* ed_prev_word():
308  *	Move to the beginning of the current word
309  *	[M-b] [b]
310  */
311 protected el_action_t
312 /*ARGSUSED*/
313 ed_prev_word(EditLine *el, wint_t c __attribute__((__unused__)))
314 {
315 
316 	if (el->el_line.cursor == el->el_line.buffer)
317 		return CC_ERROR;
318 
319 	el->el_line.cursor = c__prev_word(el->el_line.cursor,
320 	    el->el_line.buffer,
321 	    el->el_state.argument,
322 	    ce__isword);
323 
324 	if (el->el_map.type == MAP_VI)
325 		if (el->el_chared.c_vcmd.action != NOP) {
326 			cv_delfini(el);
327 			return CC_REFRESH;
328 		}
329 	return CC_CURSOR;
330 }
331 
332 
333 /* ed_prev_char():
334  *	Move to the left one character
335  *	[^B] [^B]
336  */
337 protected el_action_t
338 /*ARGSUSED*/
339 ed_prev_char(EditLine *el, wint_t c __attribute__((__unused__)))
340 {
341 
342 	if (el->el_line.cursor > el->el_line.buffer) {
343 		el->el_line.cursor -= el->el_state.argument;
344 		if (el->el_line.cursor < el->el_line.buffer)
345 			el->el_line.cursor = el->el_line.buffer;
346 
347 		if (el->el_map.type == MAP_VI)
348 			if (el->el_chared.c_vcmd.action != NOP) {
349 				cv_delfini(el);
350 				return CC_REFRESH;
351 			}
352 		return CC_CURSOR;
353 	} else
354 		return CC_ERROR;
355 }
356 
357 
358 /* ed_quoted_insert():
359  *	Add the next character typed verbatim
360  *	[^V] [^V]
361  */
362 protected el_action_t
363 ed_quoted_insert(EditLine *el, wint_t c)
364 {
365 	int num;
366 
367 	tty_quotemode(el);
368 	num = el_wgetc(el, &c);
369 	tty_noquotemode(el);
370 	if (num == 1)
371 		return ed_insert(el, c);
372 	else
373 		return ed_end_of_file(el, 0);
374 }
375 
376 
377 /* ed_digit():
378  *	Adds to argument or enters a digit
379  */
380 protected el_action_t
381 ed_digit(EditLine *el, wint_t c)
382 {
383 
384 	if (!iswdigit(c))
385 		return CC_ERROR;
386 
387 	if (el->el_state.doingarg) {
388 			/* if doing an arg, add this in... */
389 		if (el->el_state.lastcmd == EM_UNIVERSAL_ARGUMENT)
390 			el->el_state.argument = c - '0';
391 		else {
392 			if (el->el_state.argument > 1000000)
393 				return CC_ERROR;
394 			el->el_state.argument =
395 			    (el->el_state.argument * 10) + (c - '0');
396 		}
397 		return CC_ARGHACK;
398 	}
399 
400 	return ed_insert(el, c);
401 }
402 
403 
404 /* ed_argument_digit():
405  *	Digit that starts argument
406  *	For ESC-n
407  */
408 protected el_action_t
409 ed_argument_digit(EditLine *el, wint_t c)
410 {
411 
412 	if (!iswdigit(c))
413 		return CC_ERROR;
414 
415 	if (el->el_state.doingarg) {
416 		if (el->el_state.argument > 1000000)
417 			return CC_ERROR;
418 		el->el_state.argument = (el->el_state.argument * 10) +
419 		    (c - '0');
420 	} else {		/* else starting an argument */
421 		el->el_state.argument = c - '0';
422 		el->el_state.doingarg = 1;
423 	}
424 	return CC_ARGHACK;
425 }
426 
427 
428 /* ed_unassigned():
429  *	Indicates unbound character
430  *	Bound to keys that are not assigned
431  */
432 protected el_action_t
433 /*ARGSUSED*/
434 ed_unassigned(EditLine *el, wint_t c __attribute__((__unused__)))
435 {
436 
437 	return CC_ERROR;
438 }
439 
440 
441 /* ed_ignore():
442  *	Input characters that have no effect
443  *	[^C ^O ^Q ^S ^Z ^\ ^]] [^C ^O ^Q ^S ^\]
444  */
445 protected el_action_t
446 /*ARGSUSED*/
447 ed_ignore(EditLine *el __attribute__((__unused__)),
448 	      wint_t c __attribute__((__unused__)))
449 {
450 
451 	return CC_NORM;
452 }
453 
454 
455 /* ed_newline():
456  *	Execute command
457  *	[^J]
458  */
459 protected el_action_t
460 /*ARGSUSED*/
461 ed_newline(EditLine *el, wint_t c __attribute__((__unused__)))
462 {
463 
464 	re_goto_bottom(el);
465 	*el->el_line.lastchar++ = '\n';
466 	*el->el_line.lastchar = '\0';
467 	return CC_NEWLINE;
468 }
469 
470 
471 /* ed_delete_prev_char():
472  *	Delete the character to the left of the cursor
473  *	[^?]
474  */
475 protected el_action_t
476 /*ARGSUSED*/
477 ed_delete_prev_char(EditLine *el, wint_t c __attribute__((__unused__)))
478 {
479 
480 	if (el->el_line.cursor <= el->el_line.buffer)
481 		return CC_ERROR;
482 
483 	c_delbefore(el, el->el_state.argument);
484 	el->el_line.cursor -= el->el_state.argument;
485 	if (el->el_line.cursor < el->el_line.buffer)
486 		el->el_line.cursor = el->el_line.buffer;
487 	return CC_REFRESH;
488 }
489 
490 
491 /* ed_clear_screen():
492  *	Clear screen leaving current line at the top
493  *	[^L]
494  */
495 protected el_action_t
496 /*ARGSUSED*/
497 ed_clear_screen(EditLine *el, wint_t c __attribute__((__unused__)))
498 {
499 
500 	terminal_clear_screen(el);	/* clear the whole real screen */
501 	re_clear_display(el);	/* reset everything */
502 	return CC_REFRESH;
503 }
504 
505 
506 /* ed_redisplay():
507  *	Redisplay everything
508  *	^R
509  */
510 protected el_action_t
511 /*ARGSUSED*/
512 ed_redisplay(EditLine *el __attribute__((__unused__)),
513 	     wint_t c __attribute__((__unused__)))
514 {
515 
516 	return CC_REDISPLAY;
517 }
518 
519 
520 /* ed_start_over():
521  *	Erase current line and start from scratch
522  *	[^G]
523  */
524 protected el_action_t
525 /*ARGSUSED*/
526 ed_start_over(EditLine *el, wint_t c __attribute__((__unused__)))
527 {
528 
529 	ch_reset(el);
530 	return CC_REFRESH;
531 }
532 
533 
534 /* ed_sequence_lead_in():
535  *	First character in a bound sequence
536  *	Placeholder for external keys
537  */
538 protected el_action_t
539 /*ARGSUSED*/
540 ed_sequence_lead_in(EditLine *el __attribute__((__unused__)),
541 		    wint_t c __attribute__((__unused__)))
542 {
543 
544 	return CC_NORM;
545 }
546 
547 
548 /* ed_prev_history():
549  *	Move to the previous history line
550  *	[^P] [k]
551  */
552 protected el_action_t
553 /*ARGSUSED*/
554 ed_prev_history(EditLine *el, wint_t c __attribute__((__unused__)))
555 {
556 	char beep = 0;
557 	int sv_event = el->el_history.eventno;
558 
559 	el->el_chared.c_undo.len = -1;
560 	*el->el_line.lastchar = '\0';		/* just in case */
561 
562 	if (el->el_history.eventno == 0) {	/* save the current buffer
563 						 * away */
564 		(void) wcsncpy(el->el_history.buf, el->el_line.buffer,
565 		    EL_BUFSIZ);
566 		el->el_history.last = el->el_history.buf +
567 		    (el->el_line.lastchar - el->el_line.buffer);
568 	}
569 	el->el_history.eventno += el->el_state.argument;
570 
571 	if (hist_get(el) == CC_ERROR) {
572 		if (el->el_map.type == MAP_VI) {
573 			el->el_history.eventno = sv_event;
574 		}
575 		beep = 1;
576 		/* el->el_history.eventno was fixed by first call */
577 		(void) hist_get(el);
578 	}
579 	if (beep)
580 		return CC_REFRESH_BEEP;
581 	return CC_REFRESH;
582 }
583 
584 
585 /* ed_next_history():
586  *	Move to the next history line
587  *	[^N] [j]
588  */
589 protected el_action_t
590 /*ARGSUSED*/
591 ed_next_history(EditLine *el, wint_t c __attribute__((__unused__)))
592 {
593 	el_action_t beep = CC_REFRESH, rval;
594 
595 	el->el_chared.c_undo.len = -1;
596 	*el->el_line.lastchar = '\0';	/* just in case */
597 
598 	el->el_history.eventno -= el->el_state.argument;
599 
600 	if (el->el_history.eventno < 0) {
601 		el->el_history.eventno = 0;
602 		beep = CC_REFRESH_BEEP;
603 	}
604 	rval = hist_get(el);
605 	if (rval == CC_REFRESH)
606 		return beep;
607 	return rval;
608 
609 }
610 
611 
612 /* ed_search_prev_history():
613  *	Search previous in history for a line matching the current
614  *	next search history [M-P] [K]
615  */
616 protected el_action_t
617 /*ARGSUSED*/
618 ed_search_prev_history(EditLine *el, wint_t c __attribute__((__unused__)))
619 {
620 	const wchar_t *hp;
621 	int h;
622 	int found = 0;
623 
624 	el->el_chared.c_vcmd.action = NOP;
625 	el->el_chared.c_undo.len = -1;
626 	*el->el_line.lastchar = '\0';	/* just in case */
627 	if (el->el_history.eventno < 0) {
628 #ifdef DEBUG_EDIT
629 		(void) fprintf(el->el_errfile,
630 		    "e_prev_search_hist(): eventno < 0;\n");
631 #endif
632 		el->el_history.eventno = 0;
633 		return CC_ERROR;
634 	}
635 	if (el->el_history.eventno == 0) {
636 		(void) wcsncpy(el->el_history.buf, el->el_line.buffer,
637 		    EL_BUFSIZ);
638 		el->el_history.last = el->el_history.buf +
639 		    (el->el_line.lastchar - el->el_line.buffer);
640 	}
641 	if (el->el_history.ref == NULL)
642 		return CC_ERROR;
643 
644 	hp = HIST_FIRST(el);
645 	if (hp == NULL)
646 		return CC_ERROR;
647 
648 	c_setpat(el);		/* Set search pattern !! */
649 
650 	for (h = 1; h <= el->el_history.eventno; h++)
651 		hp = HIST_NEXT(el);
652 
653 	while (hp != NULL) {
654 #ifdef SDEBUG
655 		(void) fprintf(el->el_errfile, "Comparing with \"%s\"\n", hp);
656 #endif
657 		if ((wcsncmp(hp, el->el_line.buffer, (size_t)
658 			    (el->el_line.lastchar - el->el_line.buffer)) ||
659 			hp[el->el_line.lastchar - el->el_line.buffer]) &&
660 		    c_hmatch(el, hp)) {
661 			found = 1;
662 			break;
663 		}
664 		h++;
665 		hp = HIST_NEXT(el);
666 	}
667 
668 	if (!found) {
669 #ifdef SDEBUG
670 		(void) fprintf(el->el_errfile, "not found\n");
671 #endif
672 		return CC_ERROR;
673 	}
674 	el->el_history.eventno = h;
675 
676 	return hist_get(el);
677 }
678 
679 
680 /* ed_search_next_history():
681  *	Search next in history for a line matching the current
682  *	[M-N] [J]
683  */
684 protected el_action_t
685 /*ARGSUSED*/
686 ed_search_next_history(EditLine *el, wint_t c __attribute__((__unused__)))
687 {
688 	const wchar_t *hp;
689 	int h;
690 	int found = 0;
691 
692 	el->el_chared.c_vcmd.action = NOP;
693 	el->el_chared.c_undo.len = -1;
694 	*el->el_line.lastchar = '\0';	/* just in case */
695 
696 	if (el->el_history.eventno == 0)
697 		return CC_ERROR;
698 
699 	if (el->el_history.ref == NULL)
700 		return CC_ERROR;
701 
702 	hp = HIST_FIRST(el);
703 	if (hp == NULL)
704 		return CC_ERROR;
705 
706 	c_setpat(el);		/* Set search pattern !! */
707 
708 	for (h = 1; h < el->el_history.eventno && hp; h++) {
709 #ifdef SDEBUG
710 		(void) fprintf(el->el_errfile, "Comparing with \"%s\"\n", hp);
711 #endif
712 		if ((wcsncmp(hp, el->el_line.buffer, (size_t)
713 			    (el->el_line.lastchar - el->el_line.buffer)) ||
714 			hp[el->el_line.lastchar - el->el_line.buffer]) &&
715 		    c_hmatch(el, hp))
716 			found = h;
717 		hp = HIST_NEXT(el);
718 	}
719 
720 	if (!found) {		/* is it the current history number? */
721 		if (!c_hmatch(el, el->el_history.buf)) {
722 #ifdef SDEBUG
723 			(void) fprintf(el->el_errfile, "not found\n");
724 #endif
725 			return CC_ERROR;
726 		}
727 	}
728 	el->el_history.eventno = found;
729 
730 	return hist_get(el);
731 }
732 
733 
734 /* ed_prev_line():
735  *	Move up one line
736  *	Could be [k] [^p]
737  */
738 protected el_action_t
739 /*ARGSUSED*/
740 ed_prev_line(EditLine *el, wint_t c __attribute__((__unused__)))
741 {
742 	wchar_t *ptr;
743 	int nchars = c_hpos(el);
744 
745 	/*
746          * Move to the line requested
747          */
748 	if (*(ptr = el->el_line.cursor) == '\n')
749 		ptr--;
750 
751 	for (; ptr >= el->el_line.buffer; ptr--)
752 		if (*ptr == '\n' && --el->el_state.argument <= 0)
753 			break;
754 
755 	if (el->el_state.argument > 0)
756 		return CC_ERROR;
757 
758 	/*
759          * Move to the beginning of the line
760          */
761 	for (ptr--; ptr >= el->el_line.buffer && *ptr != '\n'; ptr--)
762 		continue;
763 
764 	/*
765          * Move to the character requested
766          */
767 	for (ptr++;
768 	    nchars-- > 0 && ptr < el->el_line.lastchar && *ptr != '\n';
769 	    ptr++)
770 		continue;
771 
772 	el->el_line.cursor = ptr;
773 	return CC_CURSOR;
774 }
775 
776 
777 /* ed_next_line():
778  *	Move down one line
779  *	Could be [j] [^n]
780  */
781 protected el_action_t
782 /*ARGSUSED*/
783 ed_next_line(EditLine *el, wint_t c __attribute__((__unused__)))
784 {
785 	wchar_t *ptr;
786 	int nchars = c_hpos(el);
787 
788 	/*
789          * Move to the line requested
790          */
791 	for (ptr = el->el_line.cursor; ptr < el->el_line.lastchar; ptr++)
792 		if (*ptr == '\n' && --el->el_state.argument <= 0)
793 			break;
794 
795 	if (el->el_state.argument > 0)
796 		return CC_ERROR;
797 
798 	/*
799          * Move to the character requested
800          */
801 	for (ptr++;
802 	    nchars-- > 0 && ptr < el->el_line.lastchar && *ptr != '\n';
803 	    ptr++)
804 		continue;
805 
806 	el->el_line.cursor = ptr;
807 	return CC_CURSOR;
808 }
809 
810 
811 /* ed_command():
812  *	Editline extended command
813  *	[M-X] [:]
814  */
815 protected el_action_t
816 /*ARGSUSED*/
817 ed_command(EditLine *el, wint_t c __attribute__((__unused__)))
818 {
819 	wchar_t tmpbuf[EL_BUFSIZ];
820 	int tmplen;
821 
822 	tmplen = c_gets(el, tmpbuf, L"\n: ");
823 	terminal__putc(el, '\n');
824 
825 	if (tmplen < 0 || (tmpbuf[tmplen] = 0, parse_line(el, tmpbuf)) == -1)
826 		terminal_beep(el);
827 
828 	el->el_map.current = el->el_map.key;
829 	re_clear_display(el);
830 	return CC_REFRESH;
831 }
832