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