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