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