xref: /openbsd-src/usr.bin/less/cmdbuf.c (revision 6f05df2d9be0954bec42d51d943d77bd250fb664)
1 /*
2  * Copyright (C) 1984-2012  Mark Nudelman
3  *
4  * You may distribute under the terms of either the GNU General Public
5  * License or the Less License, as specified in the README file.
6  *
7  * For more information, see the README file.
8  */
9 
10 
11 /*
12  * Functions which manipulate the command buffer.
13  * Used only by command() and related functions.
14  */
15 
16 #include "less.h"
17 #include "cmd.h"
18 #include "charset.h"
19 #if HAVE_STAT
20 #include <sys/stat.h>
21 #endif
22 
23 extern int sc_width;
24 extern int utf_mode;
25 
26 static char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */
27 static int cmd_col;		/* Current column of the cursor */
28 static int prompt_col;		/* Column of cursor just after prompt */
29 static char *cp;		/* Pointer into cmdbuf */
30 static int cmd_offset;		/* Index into cmdbuf of first displayed char */
31 static int literal;		/* Next input char should not be interpreted */
32 static int updown_match = -1;	/* Prefix length in up/down movement */
33 
34 #if TAB_COMPLETE_FILENAME
35 static int cmd_complete();
36 /*
37  * These variables are statics used by cmd_complete.
38  */
39 static int in_completion = 0;
40 static char *tk_text;
41 static char *tk_original;
42 static char *tk_ipoint;
43 static char *tk_trial;
44 static struct textlist tk_tlist;
45 #endif
46 
47 static int cmd_left();
48 static int cmd_right();
49 
50 #if SPACES_IN_FILENAMES
51 public char openquote = '"';
52 public char closequote = '"';
53 #endif
54 
55 #if CMD_HISTORY
56 
57 /* History file */
58 #define HISTFILE_FIRST_LINE      ".less-history-file:"
59 #define HISTFILE_SEARCH_SECTION  ".search"
60 #define HISTFILE_SHELL_SECTION   ".shell"
61 
62 /*
63  * A mlist structure represents a command history.
64  */
65 struct mlist
66 {
67 	struct mlist *next;
68 	struct mlist *prev;
69 	struct mlist *curr_mp;
70 	char *string;
71 	int modified;
72 };
73 
74 /*
75  * These are the various command histories that exist.
76  */
77 struct mlist mlist_search =
78 	{ &mlist_search,  &mlist_search,  &mlist_search,  NULL, 0 };
79 public void * constant ml_search = (void *) &mlist_search;
80 
81 struct mlist mlist_examine =
82 	{ &mlist_examine, &mlist_examine, &mlist_examine, NULL, 0 };
83 public void * constant ml_examine = (void *) &mlist_examine;
84 
85 #if SHELL_ESCAPE || PIPEC
86 struct mlist mlist_shell =
87 	{ &mlist_shell,   &mlist_shell,   &mlist_shell,   NULL, 0 };
88 public void * constant ml_shell = (void *) &mlist_shell;
89 #endif
90 
91 #else /* CMD_HISTORY */
92 
93 /* If CMD_HISTORY is off, these are just flags. */
94 public void * constant ml_search = (void *)1;
95 public void * constant ml_examine = (void *)2;
96 #if SHELL_ESCAPE || PIPEC
97 public void * constant ml_shell = (void *)3;
98 #endif
99 
100 #endif /* CMD_HISTORY */
101 
102 /*
103  * History for the current command.
104  */
105 static struct mlist *curr_mlist = NULL;
106 static int curr_cmdflags;
107 
108 static char cmd_mbc_buf[MAX_UTF_CHAR_LEN];
109 static int cmd_mbc_buf_len;
110 static int cmd_mbc_buf_index;
111 
112 
113 /*
114  * Reset command buffer (to empty).
115  */
116 	public void
117 cmd_reset()
118 {
119 	cp = cmdbuf;
120 	*cp = '\0';
121 	cmd_col = 0;
122 	cmd_offset = 0;
123 	literal = 0;
124 	cmd_mbc_buf_len = 0;
125 	updown_match = -1;
126 }
127 
128 /*
129  * Clear command line.
130  */
131 	public void
132 clear_cmd()
133 {
134 	cmd_col = prompt_col = 0;
135 	cmd_mbc_buf_len = 0;
136 	updown_match = -1;
137 }
138 
139 /*
140  * Display a string, usually as a prompt for input into the command buffer.
141  */
142 	public void
143 cmd_putstr(s)
144 	char *s;
145 {
146 	LWCHAR prev_ch = 0;
147 	LWCHAR ch;
148 	char *endline = s + strlen(s);
149 	while (*s != '\0')
150 	{
151 		char *ns = s;
152 		ch = step_char(&ns, +1, endline);
153 		while (s < ns)
154 			putchr(*s++);
155 		if (!utf_mode)
156 		{
157 			cmd_col++;
158 			prompt_col++;
159 		}
160 #if !SMALL
161 		else if (!is_composing_char(ch) &&
162 		           !is_combining_char(prev_ch, ch))
163 		{
164 			int width = is_wide_char(ch) ? 2 : 1;
165 			cmd_col += width;
166 			prompt_col += width;
167 		}
168 #endif /* !SMALL */
169 		prev_ch = ch;
170 	}
171 }
172 
173 /*
174  * How many characters are in the command buffer?
175  */
176 	public int
177 len_cmdbuf()
178 {
179 	char *s = cmdbuf;
180 	char *endline = s + strlen(s);
181 	int len = 0;
182 
183 	while (*s != '\0')
184 	{
185 		step_char(&s, +1, endline);
186 		len++;
187 	}
188 	return (len);
189 }
190 
191 /*
192  * Common part of cmd_step_right() and cmd_step_left().
193  */
194 	static char *
195 cmd_step_common(p, ch, len, pwidth, bswidth)
196 	char *p;
197 	LWCHAR ch;
198 	int len;
199 	int *pwidth;
200 	int *bswidth;
201 {
202 	char *pr;
203 
204 	if (len == 1)
205 	{
206 		pr = prchar((int) ch);
207 		if (pwidth != NULL || bswidth != NULL)
208 		{
209 			int len = strlen(pr);
210 			if (pwidth != NULL)
211 				*pwidth = len;
212 			if (bswidth != NULL)
213 				*bswidth = len;
214 		}
215 	}
216 #if !SMALL
217 	else
218 	{
219 		pr = prutfchar(ch);
220 		if (pwidth != NULL || bswidth != NULL)
221 		{
222 			if (is_composing_char(ch))
223 			{
224 				if (pwidth != NULL)
225 					*pwidth = 0;
226 				if (bswidth != NULL)
227 					*bswidth = 0;
228 			} else if (is_ubin_char(ch))
229 			{
230 				int len = strlen(pr);
231 				if (pwidth != NULL)
232 					*pwidth = len;
233 				if (bswidth != NULL)
234 					*bswidth = len;
235 			} else
236 			{
237 				LWCHAR prev_ch = step_char(&p, -1, cmdbuf);
238 				if (is_combining_char(prev_ch, ch))
239 				{
240 					if (pwidth != NULL)
241 						*pwidth = 0;
242 					if (bswidth != NULL)
243 						*bswidth = 0;
244 				} else
245 				{
246 					if (pwidth != NULL)
247 						*pwidth	= is_wide_char(ch)
248 							?	2
249 							:	1;
250 					if (bswidth != NULL)
251 						*bswidth = 1;
252 				}
253 			}
254 		}
255 	}
256 #endif /* !SMALL */
257 
258 	return (pr);
259 }
260 
261 /*
262  * Step a pointer one character right in the command buffer.
263  */
264 	static char *
265 cmd_step_right(pp, pwidth, bswidth)
266 	char **pp;
267 	int *pwidth;
268 	int *bswidth;
269 {
270 	char *p = *pp;
271 	LWCHAR ch = step_char(pp, +1, p + strlen(p));
272 
273 	return cmd_step_common(p, ch, *pp - p, pwidth, bswidth);
274 }
275 
276 /*
277  * Step a pointer one character left in the command buffer.
278  */
279 	static char *
280 cmd_step_left(pp, pwidth, bswidth)
281 	char **pp;
282 	int *pwidth;
283 	int *bswidth;
284 {
285 	char *p = *pp;
286 	LWCHAR ch = step_char(pp, -1, cmdbuf);
287 
288 	return cmd_step_common(*pp, ch, p - *pp, pwidth, bswidth);
289 }
290 
291 /*
292  * Repaint the line from cp onwards.
293  * Then position the cursor just after the char old_cp (a pointer into cmdbuf).
294  */
295 	static void
296 cmd_repaint(old_cp)
297 	char *old_cp;
298 {
299 	/*
300 	 * Repaint the line from the current position.
301 	 */
302 	clear_eol();
303 	while (*cp != '\0')
304 	{
305 		char *np = cp;
306 		int width;
307 		char *pr = cmd_step_right(&np, &width, NULL);
308 		if (cmd_col + width >= sc_width)
309 			break;
310 		cp = np;
311 		putstr(pr);
312 		cmd_col += width;
313 	}
314 	while (*cp != '\0')
315 	{
316 		char *np = cp;
317 		int width;
318 		char *pr = cmd_step_right(&np, &width, NULL);
319 		if (width > 0)
320 			break;
321 		cp = np;
322 		putstr(pr);
323 	}
324 
325 	/*
326 	 * Back up the cursor to the correct position.
327 	 */
328 	while (cp > old_cp)
329 		cmd_left();
330 }
331 
332 /*
333  * Put the cursor at "home" (just after the prompt),
334  * and set cp to the corresponding char in cmdbuf.
335  */
336 	static void
337 cmd_home()
338 {
339 	while (cmd_col > prompt_col)
340 	{
341 		int width, bswidth;
342 
343 		cmd_step_left(&cp, &width, &bswidth);
344 		while (bswidth-- > 0)
345 			putbs();
346 		cmd_col -= width;
347 	}
348 
349 	cp = &cmdbuf[cmd_offset];
350 }
351 
352 /*
353  * Shift the cmdbuf display left a half-screen.
354  */
355 	static void
356 cmd_lshift()
357 {
358 	char *s;
359 	char *save_cp;
360 	int cols;
361 
362 	/*
363 	 * Start at the first displayed char, count how far to the
364 	 * right we'd have to move to reach the center of the screen.
365 	 */
366 	s = cmdbuf + cmd_offset;
367 	cols = 0;
368 	while (cols < (sc_width - prompt_col) / 2 && *s != '\0')
369 	{
370 		int width;
371 		cmd_step_right(&s, &width, NULL);
372 		cols += width;
373 	}
374 	while (*s != '\0')
375 	{
376 		int width;
377 		char *ns = s;
378 		cmd_step_right(&ns, &width, NULL);
379 		if (width > 0)
380 			break;
381 		s = ns;
382 	}
383 
384 	cmd_offset = s - cmdbuf;
385 	save_cp = cp;
386 	cmd_home();
387 	cmd_repaint(save_cp);
388 }
389 
390 /*
391  * Shift the cmdbuf display right a half-screen.
392  */
393 	static void
394 cmd_rshift()
395 {
396 	char *s;
397 	char *save_cp;
398 	int cols;
399 
400 	/*
401 	 * Start at the first displayed char, count how far to the
402 	 * left we'd have to move to traverse a half-screen width
403 	 * of displayed characters.
404 	 */
405 	s = cmdbuf + cmd_offset;
406 	cols = 0;
407 	while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf)
408 	{
409 		int width;
410 		cmd_step_left(&s, &width, NULL);
411 		cols += width;
412 	}
413 
414 	cmd_offset = s - cmdbuf;
415 	save_cp = cp;
416 	cmd_home();
417 	cmd_repaint(save_cp);
418 }
419 
420 /*
421  * Move cursor right one character.
422  */
423 	static int
424 cmd_right()
425 {
426 	char *pr;
427 	char *ncp;
428 	int width;
429 
430 	if (*cp == '\0')
431 	{
432 		/* Already at the end of the line. */
433 		return (CC_OK);
434 	}
435 	ncp = cp;
436 	pr = cmd_step_right(&ncp, &width, NULL);
437 	if (cmd_col + width >= sc_width)
438 		cmd_lshift();
439 	else if (cmd_col + width == sc_width - 1 && cp[1] != '\0')
440 		cmd_lshift();
441 	cp = ncp;
442 	cmd_col += width;
443 	putstr(pr);
444 	while (*cp != '\0')
445 	{
446 		pr = cmd_step_right(&ncp, &width, NULL);
447 		if (width > 0)
448 			break;
449 		putstr(pr);
450 		cp = ncp;
451 	}
452 	return (CC_OK);
453 }
454 
455 /*
456  * Move cursor left one character.
457  */
458 	static int
459 cmd_left()
460 {
461 	char *ncp;
462 	int width, bswidth;
463 
464 	if (cp <= cmdbuf)
465 	{
466 		/* Already at the beginning of the line */
467 		return (CC_OK);
468 	}
469 	ncp = cp;
470 	while (ncp > cmdbuf)
471 	{
472 		cmd_step_left(&ncp, &width, &bswidth);
473 		if (width > 0)
474 			break;
475 	}
476 	if (cmd_col < prompt_col + width)
477 		cmd_rshift();
478 	cp = ncp;
479 	cmd_col -= width;
480 	while (bswidth-- > 0)
481 		putbs();
482 	return (CC_OK);
483 }
484 
485 /*
486  * Insert a char into the command buffer, at the current position.
487  */
488 	static int
489 cmd_ichar(cs, clen)
490 	char *cs;
491 	int clen;
492 {
493 	char *s;
494 
495 	if (strlen(cmdbuf) + clen >= sizeof(cmdbuf)-1)
496 	{
497 		/* No room in the command buffer for another char. */
498 		bell();
499 		return (CC_ERROR);
500 	}
501 
502 	/*
503 	 * Make room for the new character (shift the tail of the buffer right).
504 	 */
505 	for (s = &cmdbuf[strlen(cmdbuf)];  s >= cp;  s--)
506 		s[clen] = s[0];
507 	/*
508 	 * Insert the character into the buffer.
509 	 */
510 	for (s = cp;  s < cp + clen;  s++)
511 		*s = *cs++;
512 	/*
513 	 * Reprint the tail of the line from the inserted char.
514 	 */
515 	updown_match = -1;
516 	cmd_repaint(cp);
517 	cmd_right();
518 	return (CC_OK);
519 }
520 
521 /*
522  * Backspace in the command buffer.
523  * Delete the char to the left of the cursor.
524  */
525 	static int
526 cmd_erase()
527 {
528 	register char *s;
529 	int clen;
530 
531 	if (cp == cmdbuf)
532 	{
533 		/*
534 		 * Backspace past beginning of the buffer:
535 		 * this usually means abort the command.
536 		 */
537 		return (CC_QUIT);
538 	}
539 	/*
540 	 * Move cursor left (to the char being erased).
541 	 */
542 	s = cp;
543 	cmd_left();
544 	clen = s - cp;
545 
546 	/*
547 	 * Remove the char from the buffer (shift the buffer left).
548 	 */
549 	for (s = cp;  ;  s++)
550 	{
551 		s[0] = s[clen];
552 		if (s[0] == '\0')
553 			break;
554 	}
555 
556 	/*
557 	 * Repaint the buffer after the erased char.
558 	 */
559 	updown_match = -1;
560 	cmd_repaint(cp);
561 
562 	/*
563 	 * We say that erasing the entire command string causes us
564 	 * to abort the current command, if CF_QUIT_ON_ERASE is set.
565 	 */
566 	if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0')
567 		return (CC_QUIT);
568 	return (CC_OK);
569 }
570 
571 /*
572  * Delete the char under the cursor.
573  */
574 	static int
575 cmd_delete()
576 {
577 	if (*cp == '\0')
578 	{
579 		/* At end of string; there is no char under the cursor. */
580 		return (CC_OK);
581 	}
582 	/*
583 	 * Move right, then use cmd_erase.
584 	 */
585 	cmd_right();
586 	cmd_erase();
587 	return (CC_OK);
588 }
589 
590 /*
591  * Delete the "word" to the left of the cursor.
592  */
593 	static int
594 cmd_werase()
595 {
596 	if (cp > cmdbuf && cp[-1] == ' ')
597 	{
598 		/*
599 		 * If the char left of cursor is a space,
600 		 * erase all the spaces left of cursor (to the first non-space).
601 		 */
602 		while (cp > cmdbuf && cp[-1] == ' ')
603 			(void) cmd_erase();
604 	} else
605 	{
606 		/*
607 		 * If the char left of cursor is not a space,
608 		 * erase all the nonspaces left of cursor (the whole "word").
609 		 */
610 		while (cp > cmdbuf && cp[-1] != ' ')
611 			(void) cmd_erase();
612 	}
613 	return (CC_OK);
614 }
615 
616 /*
617  * Delete the "word" under the cursor.
618  */
619 	static int
620 cmd_wdelete()
621 {
622 	if (*cp == ' ')
623 	{
624 		/*
625 		 * If the char under the cursor is a space,
626 		 * delete it and all the spaces right of cursor.
627 		 */
628 		while (*cp == ' ')
629 			(void) cmd_delete();
630 	} else
631 	{
632 		/*
633 		 * If the char under the cursor is not a space,
634 		 * delete it and all nonspaces right of cursor (the whole word).
635 		 */
636 		while (*cp != ' ' && *cp != '\0')
637 			(void) cmd_delete();
638 	}
639 	return (CC_OK);
640 }
641 
642 /*
643  * Delete all chars in the command buffer.
644  */
645 	static int
646 cmd_kill()
647 {
648 	if (cmdbuf[0] == '\0')
649 	{
650 		/* Buffer is already empty; abort the current command. */
651 		return (CC_QUIT);
652 	}
653 	cmd_offset = 0;
654 	cmd_home();
655 	*cp = '\0';
656 	updown_match = -1;
657 	cmd_repaint(cp);
658 
659 	/*
660 	 * We say that erasing the entire command string causes us
661 	 * to abort the current command, if CF_QUIT_ON_ERASE is set.
662 	 */
663 	if (curr_cmdflags & CF_QUIT_ON_ERASE)
664 		return (CC_QUIT);
665 	return (CC_OK);
666 }
667 
668 /*
669  * Select an mlist structure to be the current command history.
670  */
671 	public void
672 set_mlist(mlist, cmdflags)
673 	void *mlist;
674 	int cmdflags;
675 {
676 #if CMD_HISTORY
677 	curr_mlist = (struct mlist *) mlist;
678 	curr_cmdflags = cmdflags;
679 
680 	/* Make sure the next up-arrow moves to the last string in the mlist. */
681 	if (curr_mlist != NULL)
682 		curr_mlist->curr_mp = curr_mlist;
683 #endif
684 }
685 
686 #if CMD_HISTORY
687 /*
688  * Move up or down in the currently selected command history list.
689  * Only consider entries whose first updown_match chars are equal to
690  * cmdbuf's corresponding chars.
691  */
692 	static int
693 cmd_updown(action)
694 	int action;
695 {
696 	char *s;
697 	struct mlist *ml;
698 
699 	if (curr_mlist == NULL)
700 	{
701 		/*
702 		 * The current command has no history list.
703 		 */
704 		bell();
705 		return (CC_OK);
706 	}
707 
708 	if (updown_match < 0)
709 	{
710 		updown_match = cp - cmdbuf;
711 	}
712 
713 	/*
714 	 * Find the next history entry which matches.
715 	 */
716 	for (ml = curr_mlist->curr_mp;;)
717 	{
718 		ml = (action == EC_UP) ? ml->prev : ml->next;
719 		if (ml == curr_mlist)
720 		{
721 			/*
722 			 * We reached the end (or beginning) of the list.
723 			 */
724 			break;
725 		}
726 		if (strncmp(cmdbuf, ml->string, updown_match) == 0)
727 		{
728 			/*
729 			 * This entry matches; stop here.
730 			 * Copy the entry into cmdbuf and echo it on the screen.
731 			 */
732 			curr_mlist->curr_mp = ml;
733 			s = ml->string;
734 			if (s == NULL)
735 				s = "";
736 			cmd_home();
737 			clear_eol();
738 			strlcpy(cmdbuf, s, sizeof(cmdbuf));
739 			for (cp = cmdbuf;  *cp != '\0';  )
740 				cmd_right();
741 			return (CC_OK);
742 		}
743 	}
744 	/*
745 	 * We didn't find a history entry that matches.
746 	 */
747 	bell();
748 	return (CC_OK);
749 }
750 #endif
751 
752 /*
753  * Add a string to a history list.
754  */
755 	public void
756 cmd_addhist(mlist, cmd)
757 	struct mlist *mlist;
758 	char *cmd;
759 {
760 #if CMD_HISTORY
761 	struct mlist *ml;
762 
763 	/*
764 	 * Don't save a trivial command.
765 	 */
766 	if (strlen(cmd) == 0)
767 		return;
768 
769 	/*
770 	 * Save the command unless it's a duplicate of the
771 	 * last command in the history.
772 	 */
773 	ml = mlist->prev;
774 	if (ml == mlist || strcmp(ml->string, cmd) != 0)
775 	{
776 		/*
777 		 * Did not find command in history.
778 		 * Save the command and put it at the end of the history list.
779 		 */
780 		ml = (struct mlist *) ecalloc(1, sizeof(struct mlist));
781 		ml->string = save(cmd);
782 		ml->next = mlist;
783 		ml->prev = mlist->prev;
784 		mlist->prev->next = ml;
785 		mlist->prev = ml;
786 	}
787 	/*
788 	 * Point to the cmd just after the just-accepted command.
789 	 * Thus, an UPARROW will always retrieve the previous command.
790 	 */
791 	mlist->curr_mp = ml->next;
792 #endif
793 }
794 
795 /*
796  * Accept the command in the command buffer.
797  * Add it to the currently selected history list.
798  */
799 	public void
800 cmd_accept()
801 {
802 #if CMD_HISTORY
803 	/*
804 	 * Nothing to do if there is no currently selected history list.
805 	 */
806 	if (curr_mlist == NULL)
807 		return;
808 	cmd_addhist(curr_mlist, cmdbuf);
809 	curr_mlist->modified = 1;
810 #endif
811 }
812 
813 /*
814  * Try to perform a line-edit function on the command buffer,
815  * using a specified char as a line-editing command.
816  * Returns:
817  *	CC_PASS	The char does not invoke a line edit function.
818  *	CC_OK	Line edit function done.
819  *	CC_QUIT	The char requests the current command to be aborted.
820  */
821 	static int
822 cmd_edit(c)
823 	int c;
824 {
825 	int action;
826 	int flags;
827 
828 #if TAB_COMPLETE_FILENAME
829 #define	not_in_completion()	in_completion = 0
830 #else
831 #define	not_in_completion()
832 #endif
833 
834 	/*
835 	 * See if the char is indeed a line-editing command.
836 	 */
837 	flags = 0;
838 #if CMD_HISTORY
839 	if (curr_mlist == NULL)
840 		/*
841 		 * No current history; don't accept history manipulation cmds.
842 		 */
843 		flags |= EC_NOHISTORY;
844 #endif
845 #if TAB_COMPLETE_FILENAME
846 	if (curr_mlist == ml_search)
847 		/*
848 		 * In a search command; don't accept file-completion cmds.
849 		 */
850 		flags |= EC_NOCOMPLETE;
851 #endif
852 
853 	action = editchar(c, flags);
854 
855 	switch (action)
856 	{
857 	case EC_RIGHT:
858 		not_in_completion();
859 		return (cmd_right());
860 	case EC_LEFT:
861 		not_in_completion();
862 		return (cmd_left());
863 	case EC_W_RIGHT:
864 		not_in_completion();
865 		while (*cp != '\0' && *cp != ' ')
866 			cmd_right();
867 		while (*cp == ' ')
868 			cmd_right();
869 		return (CC_OK);
870 	case EC_W_LEFT:
871 		not_in_completion();
872 		while (cp > cmdbuf && cp[-1] == ' ')
873 			cmd_left();
874 		while (cp > cmdbuf && cp[-1] != ' ')
875 			cmd_left();
876 		return (CC_OK);
877 	case EC_HOME:
878 		not_in_completion();
879 		cmd_offset = 0;
880 		cmd_home();
881 		cmd_repaint(cp);
882 		return (CC_OK);
883 	case EC_END:
884 		not_in_completion();
885 		while (*cp != '\0')
886 			cmd_right();
887 		return (CC_OK);
888 	case EC_INSERT:
889 		not_in_completion();
890 		return (CC_OK);
891 	case EC_BACKSPACE:
892 		not_in_completion();
893 		return (cmd_erase());
894 	case EC_LINEKILL:
895 		not_in_completion();
896 		return (cmd_kill());
897 	case EC_ABORT:
898 		not_in_completion();
899 		(void) cmd_kill();
900 		return (CC_QUIT);
901 	case EC_W_BACKSPACE:
902 		not_in_completion();
903 		return (cmd_werase());
904 	case EC_DELETE:
905 		not_in_completion();
906 		return (cmd_delete());
907 	case EC_W_DELETE:
908 		not_in_completion();
909 		return (cmd_wdelete());
910 	case EC_LITERAL:
911 		literal = 1;
912 		return (CC_OK);
913 #if CMD_HISTORY
914 	case EC_UP:
915 	case EC_DOWN:
916 		not_in_completion();
917 		return (cmd_updown(action));
918 #endif
919 #if TAB_COMPLETE_FILENAME
920 	case EC_F_COMPLETE:
921 	case EC_B_COMPLETE:
922 	case EC_EXPAND:
923 		return (cmd_complete(action));
924 #endif
925 	case EC_NOACTION:
926 		return (CC_OK);
927 	default:
928 		not_in_completion();
929 		return (CC_PASS);
930 	}
931 }
932 
933 #if TAB_COMPLETE_FILENAME
934 /*
935  * Insert a string into the command buffer, at the current position.
936  */
937 	static int
938 cmd_istr(str)
939 	char *str;
940 {
941 	char *s;
942 	int action;
943 	char *endline = str + strlen(str);
944 
945 	for (s = str;  *s != '\0';  )
946 	{
947 		char *os = s;
948 		step_char(&s, +1, endline);
949 		action = cmd_ichar(os, s - os);
950 		if (action != CC_OK)
951 		{
952 			bell();
953 			return (action);
954 		}
955 	}
956 	return (CC_OK);
957 }
958 
959 /*
960  * Find the beginning and end of the "current" word.
961  * This is the word which the cursor (cp) is inside or at the end of.
962  * Return pointer to the beginning of the word and put the
963  * cursor at the end of the word.
964  */
965 	static char *
966 delimit_word()
967 {
968 	char *word;
969 #if SPACES_IN_FILENAMES
970 	char *p;
971 	int delim_quoted = 0;
972 	int meta_quoted = 0;
973 	char *esc = get_meta_escape();
974 	int esclen = strlen(esc);
975 #endif
976 
977 	/*
978 	 * Move cursor to end of word.
979 	 */
980 	if (*cp != ' ' && *cp != '\0')
981 	{
982 		/*
983 		 * Cursor is on a nonspace.
984 		 * Move cursor right to the next space.
985 		 */
986 		while (*cp != ' ' && *cp != '\0')
987 			cmd_right();
988 	} else if (cp > cmdbuf && cp[-1] != ' ')
989 	{
990 		/*
991 		 * Cursor is on a space, and char to the left is a nonspace.
992 		 * We're already at the end of the word.
993 		 */
994 		;
995 #if 0
996 	} else
997 	{
998 		/*
999 		 * Cursor is on a space and char to the left is a space.
1000 		 * Huh? There's no word here.
1001 		 */
1002 		return (NULL);
1003 #endif
1004 	}
1005 	/*
1006 	 * Find the beginning of the word which the cursor is in.
1007 	 */
1008 	if (cp == cmdbuf)
1009 		return (NULL);
1010 #if SPACES_IN_FILENAMES
1011 	/*
1012 	 * If we have an unbalanced quote (that is, an open quote
1013 	 * without a corresponding close quote), we return everything
1014 	 * from the open quote, including spaces.
1015 	 */
1016 	for (word = cmdbuf;  word < cp;  word++)
1017 		if (*word != ' ')
1018 			break;
1019 	if (word >= cp)
1020 		return (cp);
1021 	for (p = cmdbuf;  p < cp;  p++)
1022 	{
1023 		if (meta_quoted)
1024 		{
1025 			meta_quoted = 0;
1026 		} else if (esclen > 0 && p + esclen < cp &&
1027 		           strncmp(p, esc, esclen) == 0)
1028 		{
1029 			meta_quoted = 1;
1030 			p += esclen - 1;
1031 		} else if (delim_quoted)
1032 		{
1033 			if (*p == closequote)
1034 				delim_quoted = 0;
1035 		} else /* (!delim_quoted) */
1036 		{
1037 			if (*p == openquote)
1038 				delim_quoted = 1;
1039 			else if (*p == ' ')
1040 				word = p+1;
1041 		}
1042 	}
1043 #endif
1044 	return (word);
1045 }
1046 
1047 /*
1048  * Set things up to enter completion mode.
1049  * Expand the word under the cursor into a list of filenames
1050  * which start with that word, and set tk_text to that list.
1051  */
1052 	static void
1053 init_compl()
1054 {
1055 	char *word;
1056 	char c;
1057 
1058 	/*
1059 	 * Get rid of any previous tk_text.
1060 	 */
1061 	if (tk_text != NULL)
1062 	{
1063 		free(tk_text);
1064 		tk_text = NULL;
1065 	}
1066 	/*
1067 	 * Find the original (uncompleted) word in the command buffer.
1068 	 */
1069 	word = delimit_word();
1070 	if (word == NULL)
1071 		return;
1072 	/*
1073 	 * Set the insertion point to the point in the command buffer
1074 	 * where the original (uncompleted) word now sits.
1075 	 */
1076 	tk_ipoint = word;
1077 	/*
1078 	 * Save the original (uncompleted) word
1079 	 */
1080 	if (tk_original != NULL)
1081 		free(tk_original);
1082 	tk_original = (char *) ecalloc(cp-word+1, sizeof(char));
1083 	strncpy(tk_original, word, cp-word);
1084 	/*
1085 	 * Get the expanded filename.
1086 	 * This may result in a single filename, or
1087 	 * a blank-separated list of filenames.
1088 	 */
1089 	c = *cp;
1090 	*cp = '\0';
1091 	if (*word != openquote)
1092 	{
1093 		tk_text = fcomplete(word);
1094 	} else
1095 	{
1096 #if MSDOS_COMPILER
1097 		char *qword = NULL;
1098 #else
1099 		char *qword = shell_quote(word+1);
1100 #endif
1101 		if (qword == NULL)
1102 			tk_text = fcomplete(word+1);
1103 		else
1104 		{
1105 			tk_text = fcomplete(qword);
1106 			free(qword);
1107 		}
1108 	}
1109 	*cp = c;
1110 }
1111 
1112 /*
1113  * Return the next word in the current completion list.
1114  */
1115 	static char *
1116 next_compl(action, prev)
1117 	int action;
1118 	char *prev;
1119 {
1120 	switch (action)
1121 	{
1122 	case EC_F_COMPLETE:
1123 		return (forw_textlist(&tk_tlist, prev));
1124 	case EC_B_COMPLETE:
1125 		return (back_textlist(&tk_tlist, prev));
1126 	}
1127 	/* Cannot happen */
1128 	return ("?");
1129 }
1130 
1131 /*
1132  * Complete the filename before (or under) the cursor.
1133  * cmd_complete may be called multiple times.  The global in_completion
1134  * remembers whether this call is the first time (create the list),
1135  * or a subsequent time (step thru the list).
1136  */
1137 	static int
1138 cmd_complete(action)
1139 	int action;
1140 {
1141 	char *s;
1142 
1143 	if (!in_completion || action == EC_EXPAND)
1144 	{
1145 		/*
1146 		 * Expand the word under the cursor and
1147 		 * use the first word in the expansion
1148 		 * (or the entire expansion if we're doing EC_EXPAND).
1149 		 */
1150 		init_compl();
1151 		if (tk_text == NULL)
1152 		{
1153 			bell();
1154 			return (CC_OK);
1155 		}
1156 		if (action == EC_EXPAND)
1157 		{
1158 			/*
1159 			 * Use the whole list.
1160 			 */
1161 			tk_trial = tk_text;
1162 		} else
1163 		{
1164 			/*
1165 			 * Use the first filename in the list.
1166 			 */
1167 			in_completion = 1;
1168 			init_textlist(&tk_tlist, tk_text);
1169 			tk_trial = next_compl(action, (char*)NULL);
1170 		}
1171 	} else
1172 	{
1173 		/*
1174 		 * We already have a completion list.
1175 		 * Use the next/previous filename from the list.
1176 		 */
1177 		tk_trial = next_compl(action, tk_trial);
1178 	}
1179 
1180   	/*
1181   	 * Remove the original word, or the previous trial completion.
1182   	 */
1183 	while (cp > tk_ipoint)
1184 		(void) cmd_erase();
1185 
1186 	if (tk_trial == NULL)
1187 	{
1188 		/*
1189 		 * There are no more trial completions.
1190 		 * Insert the original (uncompleted) filename.
1191 		 */
1192 		in_completion = 0;
1193 		if (cmd_istr(tk_original) != CC_OK)
1194 			goto fail;
1195 	} else
1196 	{
1197 		/*
1198 		 * Insert trial completion.
1199 		 */
1200 		if (cmd_istr(tk_trial) != CC_OK)
1201 			goto fail;
1202 		/*
1203 		 * If it is a directory, append a slash.
1204 		 */
1205 		if (is_dir(tk_trial))
1206 		{
1207 			if (cp > cmdbuf && cp[-1] == closequote)
1208 				(void) cmd_erase();
1209 			s = lgetenv("LESSSEPARATOR");
1210 			if (s == NULL)
1211 				s = PATHNAME_SEP;
1212 			if (cmd_istr(s) != CC_OK)
1213 				goto fail;
1214 		}
1215 	}
1216 
1217 	return (CC_OK);
1218 
1219 fail:
1220 	in_completion = 0;
1221 	bell();
1222 	return (CC_OK);
1223 }
1224 
1225 #endif /* TAB_COMPLETE_FILENAME */
1226 
1227 /*
1228  * Process a single character of a multi-character command, such as
1229  * a number, or the pattern of a search command.
1230  * Returns:
1231  *	CC_OK		The char was accepted.
1232  *	CC_QUIT		The char requests the command to be aborted.
1233  *	CC_ERROR	The char could not be accepted due to an error.
1234  */
1235 	public int
1236 cmd_char(c)
1237 	int c;
1238 {
1239 	int action;
1240 	int len;
1241 
1242 	if (!utf_mode)
1243 	{
1244 		cmd_mbc_buf[0] = c;
1245 		len = 1;
1246 	}
1247 #if !SMALL
1248 	else
1249 	{
1250 		/* Perform strict validation in all possible cases.  */
1251 		if (cmd_mbc_buf_len == 0)
1252 		{
1253 		 retry:
1254 			cmd_mbc_buf_index = 1;
1255 			*cmd_mbc_buf = c;
1256 			if (IS_ASCII_OCTET(c))
1257 				cmd_mbc_buf_len = 1;
1258 			else if (IS_UTF8_LEAD(c))
1259 			{
1260 				cmd_mbc_buf_len = utf_len(c);
1261 				return (CC_OK);
1262 			} else
1263 			{
1264 				/* UTF8_INVALID or stray UTF8_TRAIL */
1265 				bell();
1266 				return (CC_ERROR);
1267 			}
1268 		} else if (IS_UTF8_TRAIL(c))
1269 		{
1270 			cmd_mbc_buf[cmd_mbc_buf_index++] = c;
1271 			if (cmd_mbc_buf_index < cmd_mbc_buf_len)
1272 				return (CC_OK);
1273 			if (!is_utf8_well_formed(cmd_mbc_buf))
1274 			{
1275 				/* complete, but not well formed (non-shortest form), sequence */
1276 				cmd_mbc_buf_len = 0;
1277 				bell();
1278 				return (CC_ERROR);
1279 			}
1280 		} else
1281 		{
1282 			/* Flush incomplete (truncated) sequence.  */
1283 			cmd_mbc_buf_len = 0;
1284 			bell();
1285 			/* Handle new char.  */
1286 			goto retry;
1287 		}
1288 
1289 		len = cmd_mbc_buf_len;
1290 		cmd_mbc_buf_len = 0;
1291 	}
1292 #endif /* !SMALL */
1293 
1294 	if (literal)
1295 	{
1296 		/*
1297 		 * Insert the char, even if it is a line-editing char.
1298 		 */
1299 		literal = 0;
1300 		return (cmd_ichar(cmd_mbc_buf, len));
1301 	}
1302 
1303 	/*
1304 	 * See if it is a line-editing character.
1305 	 */
1306 	if (in_mca() && len == 1)
1307 	{
1308 		action = cmd_edit(c);
1309 		switch (action)
1310 		{
1311 		case CC_OK:
1312 		case CC_QUIT:
1313 			return (action);
1314 		case CC_PASS:
1315 			break;
1316 		}
1317 	}
1318 
1319 	/*
1320 	 * Insert the char into the command buffer.
1321 	 */
1322 	return (cmd_ichar(cmd_mbc_buf, len));
1323 }
1324 
1325 /*
1326  * Return the number currently in the command buffer.
1327  */
1328 	public LINENUM
1329 cmd_int(frac)
1330 	long *frac;
1331 {
1332 	char *p;
1333 	LINENUM n = 0;
1334 	int err;
1335 
1336 	for (p = cmdbuf;  *p >= '0' && *p <= '9';  p++)
1337 		n = (n * 10) + (*p - '0');
1338 	*frac = 0;
1339 	if (*p++ == '.')
1340 	{
1341 		*frac = getfraction(&p, NULL, &err);
1342 		/* {{ do something if err is set? }} */
1343 	}
1344 	return (n);
1345 }
1346 
1347 /*
1348  * Return a pointer to the command buffer.
1349  */
1350 	public char *
1351 get_cmdbuf()
1352 {
1353 	return (cmdbuf);
1354 }
1355 
1356 #if CMD_HISTORY
1357 /*
1358  * Return the last (most recent) string in the current command history.
1359  */
1360 	public char *
1361 cmd_lastpattern()
1362 {
1363 	if (curr_mlist == NULL)
1364 		return (NULL);
1365 	return (curr_mlist->curr_mp->prev->string);
1366 }
1367 #endif
1368 
1369 #if CMD_HISTORY
1370 /*
1371  * Get the name of the history file.
1372  */
1373 	static char *
1374 histfile_name()
1375 {
1376 	char *home;
1377 	char *name;
1378 	int len;
1379 
1380 	/* See if filename is explicitly specified by $LESSHISTFILE. */
1381 	name = lgetenv("LESSHISTFILE");
1382 	if (name != NULL && *name != '\0')
1383 	{
1384 		if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0)
1385 			/* $LESSHISTFILE == "-" means don't use a history file. */
1386 			return (NULL);
1387 		return (save(name));
1388 	}
1389 
1390 	/* Otherwise, file is in $HOME if enabled. */
1391 	if (strcmp (LESSHISTFILE, "-") == 0)
1392 		return (NULL);
1393 	home = lgetenv("HOME");
1394 	if (home == NULL || *home == '\0')
1395 	{
1396 #if OS2
1397 		home = lgetenv("INIT");
1398 		if (home == NULL || *home == '\0')
1399 #endif
1400 			return (NULL);
1401 	}
1402 	len = strlen(home) + strlen(LESSHISTFILE) + 2;
1403 	name = (char *) ecalloc(len, sizeof(char));
1404 	SNPRINTF2(name, len, "%s/%s", home, LESSHISTFILE);
1405 	return (name);
1406 }
1407 #endif /* CMD_HISTORY */
1408 
1409 /*
1410  * Initialize history from a .lesshist file.
1411  */
1412 	public void
1413 init_cmdhist()
1414 {
1415 #if CMD_HISTORY
1416 	struct mlist *ml = NULL;
1417 	char line[CMDBUF_SIZE];
1418 	char *filename;
1419 	FILE *f;
1420 	char *p;
1421 
1422 	filename = histfile_name();
1423 	if (filename == NULL)
1424 		return;
1425 	f = fopen(filename, "r");
1426 	free(filename);
1427 	if (f == NULL)
1428 		return;
1429 	if (fgets(line, sizeof(line), f) == NULL ||
1430 	    strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0)
1431 	{
1432 		fclose(f);
1433 		return;
1434 	}
1435 	while (fgets(line, sizeof(line), f) != NULL)
1436 	{
1437 		for (p = line;  *p != '\0';  p++)
1438 		{
1439 			if (*p == '\n' || *p == '\r')
1440 			{
1441 				*p = '\0';
1442 				break;
1443 			}
1444 		}
1445 		if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0)
1446 			ml = &mlist_search;
1447 		else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0)
1448 		{
1449 #if SHELL_ESCAPE || PIPEC
1450 			ml = &mlist_shell;
1451 #else
1452 			ml = NULL;
1453 #endif
1454 		} else if (*line == '"')
1455 		{
1456 			if (ml != NULL)
1457 				cmd_addhist(ml, line+1);
1458 		}
1459 	}
1460 	fclose(f);
1461 #endif /* CMD_HISTORY */
1462 }
1463 
1464 /*
1465  *
1466  */
1467 #if CMD_HISTORY
1468 	static void
1469 save_mlist(ml, f)
1470 	struct mlist *ml;
1471 	FILE *f;
1472 {
1473 	int histsize = 0;
1474 	int n;
1475 	char *s;
1476 
1477 	s = lgetenv("LESSHISTSIZE");
1478 	if (s != NULL)
1479 		histsize = atoi(s);
1480 	if (histsize == 0)
1481 		histsize = 100;
1482 
1483 	ml = ml->prev;
1484 	for (n = 0;  n < histsize;  n++)
1485 	{
1486 		if (ml->string == NULL)
1487 			break;
1488 		ml = ml->prev;
1489 	}
1490 	for (ml = ml->next;  ml->string != NULL;  ml = ml->next)
1491 		fprintf(f, "\"%s\n", ml->string);
1492 }
1493 #endif /* CMD_HISTORY */
1494 
1495 /*
1496  *
1497  */
1498 	public void
1499 save_cmdhist()
1500 {
1501 #if CMD_HISTORY
1502 	char *filename;
1503 	FILE *f;
1504 	int modified = 0;
1505 
1506 	if (mlist_search.modified)
1507 		modified = 1;
1508 #if SHELL_ESCAPE || PIPEC
1509 	if (mlist_shell.modified)
1510 		modified = 1;
1511 #endif
1512 	if (!modified)
1513 		return;
1514 	filename = histfile_name();
1515 	if (filename == NULL)
1516 		return;
1517 	f = fopen(filename, "w");
1518 	free(filename);
1519 	if (f == NULL)
1520 		return;
1521 #if HAVE_FCHMOD
1522 {
1523 	/* Make history file readable only by owner. */
1524 	int do_chmod = 1;
1525 #if HAVE_STAT
1526 	struct stat statbuf;
1527 	int r = fstat(fileno(f), &statbuf);
1528 	if (r < 0 || !S_ISREG(statbuf.st_mode))
1529 		/* Don't chmod if not a regular file. */
1530 		do_chmod = 0;
1531 #endif
1532 	if (do_chmod)
1533 		fchmod(fileno(f), 0600);
1534 }
1535 #endif
1536 
1537 	fprintf(f, "%s\n", HISTFILE_FIRST_LINE);
1538 
1539 	fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION);
1540 	save_mlist(&mlist_search, f);
1541 
1542 #if SHELL_ESCAPE || PIPEC
1543 	fprintf(f, "%s\n", HISTFILE_SHELL_SECTION);
1544 	save_mlist(&mlist_shell, f);
1545 #endif
1546 
1547 	fclose(f);
1548 #endif /* CMD_HISTORY */
1549 }
1550