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