xref: /openbsd-src/usr.bin/less/cmdbuf.c (revision a4afd6dad3fba28f80e70208181c06c482259988)
1 /*
2  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice in the documentation and/or other materials provided with
12  *    the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
20  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
21  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 
28 /*
29  * Functions which manipulate the command buffer.
30  * Used only by command() and related functions.
31  */
32 
33 #include "less.h"
34 #include "cmd.h"
35 
36 extern int sc_width;
37 
38 static char cmdbuf[120];	/* Buffer for holding a multi-char command */
39 static int cmd_col;		/* Current column of the multi-char command */
40 static char *cp;		/* Pointer into cmdbuf */
41 static int literal;
42 
43 #if TAB_COMPLETE_FILENAME
44 static int cmd_complete();
45 /*
46  * These variables are statics used by cmd_complete.
47  */
48 static int in_completion = 0;
49 static char *tk_text;
50 static char *tk_original;
51 static char *tk_ipoint;
52 static char *tk_trial;
53 static struct textlist tk_tlist;
54 #endif
55 
56 #if CMD_HISTORY
57 /*
58  * A mlist structure represents a command history.
59  */
60 struct mlist
61 {
62 	struct mlist *next;
63 	struct mlist *prev;
64 	struct mlist *curr_mp;
65 	char *string;
66 };
67 
68 /*
69  * These are the various command histories that exist.
70  */
71 struct mlist mlist_search =
72 	{ &mlist_search,  &mlist_search,  &mlist_search,  NULL };
73 public void *ml_search = (void *) &mlist_search;
74 struct mlist mlist_examine =
75 	{ &mlist_examine, &mlist_examine, &mlist_examine, NULL };
76 public void *ml_examine = (void *) &mlist_examine;
77 #if SHELL_ESCAPE || PIPEC
78 struct mlist mlist_shell =
79 	{ &mlist_shell,   &mlist_shell,   &mlist_shell,   NULL };
80 public void *ml_shell = (void *) &mlist_shell;
81 #endif /* SHELL_ESCAPE || PIPEC */
82 
83 /*
84  * History for the current command.
85  */
86 static struct mlist *curr_mlist = NULL;
87 
88 #endif /* CMD_HISTORY */
89 
90 /*
91  * Reset command buffer (to empty).
92  */
93 	public void
94 cmd_reset()
95 {
96 	cp = cmdbuf;
97 	*cp = '\0';
98 	cmd_col = 0;
99 	literal = 0;
100 }
101 
102 /*
103  * How many characters are in the command buffer?
104  */
105 	public int
106 len_cmdbuf()
107 {
108 	return (strlen(cmdbuf));
109 }
110 
111 /*
112  * Backspace in the command buffer.
113  * Delete the char to the left of the cursor.
114  */
115 	static int
116 cmd_erase()
117 {
118 	register char *s;
119 	char *p;
120 	int col;
121 
122 	if (cp == cmdbuf)
123 	{
124 		/*
125 		 * Backspace past beginning of the buffer:
126 		 * this usually means abort the command.
127 		 */
128 		return (CC_QUIT);
129 	}
130 	/*
131 	 * Back up the pointer.
132 	 */
133 	--cp;
134 	/*
135 	 * Remember the current cursor column and
136 	 * set it back the width of the char being erased.
137 	 */
138 	col = cmd_col;
139 	p = prchar(*cp);
140 	cmd_col -= strlen(p);
141 	/*
142 	 * Shift left the buffer after the erased char.
143 	 */
144 	for (s = cp;  *s != '\0';  s++)
145 		s[0] = s[1];
146 	/*
147 	 * Back up the cursor to the position of the erased char,
148 	 * clear the tail of the line,
149 	 * and reprint the line after the erased char.
150 	 */
151 	while (col > cmd_col)
152 	{
153 		putbs();
154 		col--;
155 	}
156 	clear_eol();
157 	for (s = cp;  *s != '\0';  s++)
158 	{
159 		p = prchar(*s);
160 		putstr(p);
161 		col += strlen(p);
162 	}
163 	/*
164 	 * Back up the cursor again.
165 	 */
166 	while (col > cmd_col)
167 	{
168 		putbs();
169 		col--;
170 	}
171 
172 	/*
173 	 * This is rather weird.
174 	 * We say that erasing the entire command string causes us
175 	 * to abort the current command, BUT ONLY IF there is no history
176 	 * for this type of command.  This causes commands like search (/)
177 	 * and edit (:e) to stay active even if we erase the entire string,
178 	 * but commands like <digit> and - go away when we erase the string.
179 	 * (See same thing in cmd_kill.)
180 	 */
181 	if (curr_mlist == NULL && cp == cmdbuf && *cp == '\0')
182 		return (CC_QUIT);
183 	return (CC_OK);
184 }
185 
186 /*
187  * Delete the char under the cursor.
188  */
189 	static int
190 cmd_delete()
191 {
192 	char *p;
193 
194 	if (*cp == '\0')
195 	{
196 		/*
197 		 * At end of string; there is no char under the cursor.
198 		 */
199 		return (CC_OK);
200 	}
201 	/*
202 	 * Move right, then use cmd_erase.
203 	 */
204 	p = prchar(*cp);
205 	cp++;
206 	putstr(p);
207 	cmd_col += strlen(p);
208 	cmd_erase();
209 	return (CC_OK);
210 }
211 
212 /*
213  * Delete the "word" to the left of the cursor.
214  */
215 	static int
216 cmd_werase()
217 {
218 	if (cp > cmdbuf && cp[-1] == ' ')
219 	{
220 		/*
221 		 * If the char left of cursor is a space,
222 		 * erase all the spaces left of cursor (to the first non-space).
223 		 */
224 		while (cp > cmdbuf && cp[-1] == ' ')
225 			(void) cmd_erase();
226 	} else
227 	{
228 		/*
229 		 * If the char left of cursor is not a space,
230 		 * erase all the nonspaces left of cursor (the whole "word").
231 		 */
232 		while (cp > cmdbuf && cp[-1] != ' ')
233 			(void) cmd_erase();
234 	}
235 	return (CC_OK);
236 }
237 
238 /*
239  * Delete the "word" under the cursor.
240  */
241 	static int
242 cmd_wdelete()
243 {
244 	if (*cp == ' ')
245 	{
246 		/*
247 		 * If the char under the cursor is a space,
248 		 * delete it and all the spaces right of cursor.
249 		 */
250 		while (*cp == ' ')
251 			(void) cmd_delete();
252 	} else
253 	{
254 		/*
255 		 * If the char under the cursor is not a space,
256 		 * delete it and all nonspaces right of cursor (the whole word).
257 		 */
258 		while (*cp != ' ' && *cp != '\0')
259 			(void) cmd_delete();
260 	}
261 	return (CC_OK);
262 }
263 
264 /*
265  * Move cursor to start of command buffer.
266  */
267 	static int
268 cmd_home()
269 {
270 	char *p;
271 
272 	/*
273 	 * Back up until we hit start of buffer.
274 	 */
275 	while (cp > cmdbuf)
276 	{
277 		cp--;
278 		p = prchar(*cp);
279 		cmd_col -= strlen(p);
280 		while (*p++ != '\0')
281 			putbs();
282 	}
283 	return (CC_OK);
284 }
285 
286 /*
287  * Delete all chars in the command buffer.
288  */
289 	static int
290 cmd_kill()
291 {
292 	if (cmdbuf[0] == '\0')
293 	{
294 		/*
295 		 * Buffer is already empty; abort the current command.
296 		 */
297 		return (CC_QUIT);
298 	}
299 	(void) cmd_home();
300 	*cp = '\0';
301 	clear_eol();
302 	/*
303 	 * Same weirdness as in cmd_erase.
304 	 * If the current command has no history, abort the current command.
305 	 */
306 	if (curr_mlist == NULL)
307 		return (CC_QUIT);
308 	return (CC_OK);
309 }
310 
311 /*
312  * Move cursor right one character.
313  */
314 	static int
315 cmd_right()
316 {
317 	char *p;
318 
319 	if (*cp == '\0')
320 	{
321 		/*
322 		 * Already at the end of the line.
323 		 */
324 		return (CC_OK);
325 	}
326 	p = prchar(*cp);
327 	cp++;
328 	putstr(p);
329 	cmd_col += strlen(p);
330 	return (CC_OK);
331 }
332 
333 /*
334  * Move cursor left one character.
335  */
336 	static int
337 cmd_left()
338 {
339 	char *p;
340 
341 	if (cp <= cmdbuf)
342 	{
343 		/* Already at the beginning of the line */
344 		return (CC_OK);
345 	}
346 	cp--;
347 	p = prchar(*cp);
348 	cmd_col -= strlen(p);
349 	while (*p++ != '\0')
350 		putbs();
351 	return (CC_OK);
352 }
353 
354 #if CMD_HISTORY
355 /*
356  * Select an mlist structure to be the current command history.
357  */
358 	public void
359 set_mlist(mlist)
360 	void *mlist;
361 {
362 	curr_mlist = (struct mlist *) mlist;
363 }
364 
365 /*
366  * Move up or down in the currently selected command history list.
367  */
368 	static int
369 cmd_updown(action)
370 	int action;
371 {
372 	char *p;
373 	char *s;
374 
375 	if (curr_mlist == NULL)
376 	{
377 		/*
378 		 * The current command has no history list.
379 		 */
380 		bell();
381 		return (CC_OK);
382 	}
383 	cmd_home();
384 	clear_eol();
385 	/*
386 	 * Move curr_mp to the next/prev entry.
387 	 */
388 	if (action == EC_UP)
389 		curr_mlist->curr_mp = curr_mlist->curr_mp->prev;
390 	else
391 		curr_mlist->curr_mp = curr_mlist->curr_mp->next;
392 	/*
393 	 * Copy the entry into cmdbuf and echo it on the screen.
394 	 */
395 	s = curr_mlist->curr_mp->string;
396 	if (s == NULL)
397 		s = "";
398 	for (cp = cmdbuf;  *s != '\0';  s++, cp++)
399 	{
400 		*cp = *s;
401 		p = prchar(*cp);
402 		cmd_col += strlen(p);
403 		putstr(p);
404 	}
405 	*cp = '\0';
406 	return (CC_OK);
407 }
408 
409 /*
410  * Accept the command in the command buffer.
411  * Add it to the currently selected history list.
412  */
413 	public void
414 cmd_accept()
415 {
416 	struct mlist *ml;
417 
418 	/*
419 	 * Nothing to do if there is no currently selected history list.
420 	 */
421 	if (curr_mlist == NULL)
422 		return;
423 	/*
424 	 * Don't save a trivial command.
425 	 */
426 	if (strlen(cmdbuf) == 0)
427 		return;
428 	/*
429 	 * Don't save if a duplicate of a command which is already in the history.
430 	 * But select the one already in the history to be current.
431 	 */
432 	for (ml = curr_mlist->next;  ml != curr_mlist;  ml = ml->next)
433 	{
434 		if (strcmp(ml->string, cmdbuf) == 0)
435 			break;
436 	}
437 	if (ml == curr_mlist)
438 	{
439 		/*
440 		 * Did not find command in history.
441 		 * Save the command and put it at the end of the history list.
442 		 */
443 		ml = (struct mlist *) ecalloc(1, sizeof(struct mlist));
444 		ml->string = save(cmdbuf);
445 		ml->next = curr_mlist;
446 		ml->prev = curr_mlist->prev;
447 		curr_mlist->prev->next = ml;
448 		curr_mlist->prev = ml;
449 	}
450 	/*
451 	 * Point to the cmd just after the just-accepted command.
452 	 * Thus, an UPARROW will always retrieve the previous command.
453 	 */
454 	curr_mlist->curr_mp = ml->next;
455 }
456 #endif
457 
458 /*
459  * Try to perform a line-edit function on the command buffer,
460  * using a specified char as a line-editing command.
461  * Returns:
462  *	CC_PASS	The char does not invoke a line edit function.
463  *	CC_OK	Line edit function done.
464  *	CC_QUIT	The char requests the current command to be aborted.
465  */
466 	static int
467 cmd_edit(c)
468 	int c;
469 {
470 	int action;
471 	int flags;
472 
473 #if TAB_COMPLETE_FILENAME
474 #define	not_in_completion()	in_completion = 0
475 #else
476 #define	not_in_completion()
477 #endif
478 
479 	/*
480 	 * See if the char is indeed a line-editing command.
481 	 */
482 	flags = 0;
483 	if (curr_mlist == NULL)
484 		/*
485 		 * No current history; don't accept history manipulation cmds.
486 		 */
487 		flags |= EC_NOHISTORY;
488 	if (curr_mlist == &mlist_search)
489 		/*
490 		 * In a search command; don't accept file-completion cmds.
491 		 */
492 		flags |= EC_NOCOMPLETE;
493 
494 	action = editchar(c, flags);
495 
496 	switch (action)
497 	{
498 	case EC_RIGHT:
499 		not_in_completion();
500 		return (cmd_right());
501 	case EC_LEFT:
502 		not_in_completion();
503 		return (cmd_left());
504 	case EC_W_RIGHT:
505 		not_in_completion();
506 		while (*cp != '\0' && *cp != ' ')
507 			cmd_right();
508 		while (*cp == ' ')
509 			cmd_right();
510 		return (CC_OK);
511 	case EC_W_LEFT:
512 		not_in_completion();
513 		while (cp > cmdbuf && cp[-1] == ' ')
514 			cmd_left();
515 		while (cp > cmdbuf && cp[-1] != ' ')
516 			cmd_left();
517 		return (CC_OK);
518 	case EC_HOME:
519 		not_in_completion();
520 		return (cmd_home());
521 	case EC_END:
522 		not_in_completion();
523 		while (*cp != '\0')
524 			cmd_right();
525 		return (CC_OK);
526 	case EC_INSERT:
527 		not_in_completion();
528 		return (CC_OK);
529 	case EC_BACKSPACE:
530 		not_in_completion();
531 		return (cmd_erase());
532 	case EC_LINEKILL:
533 		not_in_completion();
534 		return (cmd_kill());
535 	case EC_W_BACKSPACE:
536 		not_in_completion();
537 		return (cmd_werase());
538 	case EC_DELETE:
539 		not_in_completion();
540 		return (cmd_delete());
541 	case EC_W_DELETE:
542 		not_in_completion();
543 		return (cmd_wdelete());
544 	case EC_LITERAL:
545 		literal = 1;
546 		return (CC_OK);
547 #if CMD_HISTORY
548 	case EC_UP:
549 	case EC_DOWN:
550 		not_in_completion();
551 		return (cmd_updown(action));
552 #endif
553 #if TAB_COMPLETE_FILENAME
554 	case EC_F_COMPLETE:
555 	case EC_B_COMPLETE:
556 	case EC_EXPAND:
557 		return (cmd_complete(action));
558 #endif
559 	default:
560 		not_in_completion();
561 		return (CC_PASS);
562 	}
563 }
564 
565 /*
566  * Insert a char into the command buffer, at the current position.
567  */
568 	static int
569 cmd_ichar(c)
570 	int c;
571 {
572 	int col;
573 	char *p;
574 	char *s;
575 
576 	if (strlen(cmdbuf) >= sizeof(cmdbuf)-2)
577 	{
578 		/*
579 		 * No room in the command buffer for another char.
580 		 */
581 		bell();
582 		return (CC_ERROR);
583 	}
584 
585 	/*
586 	 * Remember the current cursor column and
587 	 * move it forward the width of the char being inserted.
588 	 */
589 	col = cmd_col;
590 	p = prchar(c);
591 	cmd_col += strlen(p);
592 	if (cmd_col >= sc_width-1)
593 	{
594 		cmd_col -= strlen(p);
595 		bell();
596 		return (CC_ERROR);
597 	}
598 	/*
599 	 * Insert the character in the string.
600 	 * First, make room for the new char.
601 	 */
602 	for (s = &cmdbuf[strlen(cmdbuf)];  s >= cp;  s--)
603 		s[1] = s[0];
604 	*cp++ = c;
605 	/*
606 	 * Reprint the tail of the line after the inserted char.
607 	 */
608 	clear_eol();
609 	for (s = cp-1;  *s != '\0';  s++)
610 	{
611 		p = prchar(*s);
612 		col += strlen(p);
613 		if (col >= sc_width-1)
614 		{
615 			/*
616 			 * Oops.  There is no room on the screen
617 			 * for the new char.  Back up the cursor to
618 			 * just after the inserted char and erase it.
619 			 */
620 			col -= strlen(p);
621 			while (col > cmd_col)
622 			{
623 				putbs();
624 				col--;
625 			}
626 			(void) cmd_erase();
627 			bell();
628 			return (CC_ERROR);
629 		}
630 		putstr(p);
631 	}
632 	/*
633 	 * Back up the cursor to just after the inserted char.
634 	 */
635 	while (col > cmd_col)
636 	{
637 		putbs();
638 		col--;
639 	}
640 	return (CC_OK);
641 }
642 
643 #if TAB_COMPLETE_FILENAME
644 /*
645  * Insert a string into the command buffer, at the current position.
646  */
647 	static int
648 cmd_istr(str)
649 	char *str;
650 {
651 	char *s;
652 	int action;
653 
654 	for (s = str;  *s != '\0';  s++)
655 	{
656 		action = cmd_ichar(*s);
657 		if (action != CC_OK)
658 		{
659 			bell();
660 			return (action);
661 		}
662 	}
663 	return (CC_OK);
664 }
665 
666 /*
667  * Find the beginning and end of the "current" word.
668  * This is the word which the cursor (cp) is inside or at the end of.
669  * Return pointer to the beginning of the word and put the
670  * cursor at the end of the word.
671  */
672 	static char *
673 delimit_word()
674 {
675 	char *word;
676 
677 	/*
678 	 * Move cursor to end of word.
679 	 */
680 	if (*cp != ' ' && *cp != '\0')
681 	{
682 		/*
683 		 * Cursor is on a nonspace.
684 		 * Move cursor right to the next space.
685 		 */
686 		while (*cp != ' ' && *cp != '\0')
687 			cmd_right();
688 	} else if (cp > cmdbuf && cp[-1] != ' ')
689 	{
690 		/*
691 		 * Cursor is on a space, and char to the left is a nonspace.
692 		 * We're already at the end of the word.
693 		 */
694 		;
695 	} else
696 	{
697 		/*
698 		 * Cursor is on a space and char to the left is a space.
699 		 * Huh? There's no word here.
700 		 */
701 		return (NULL);
702 	}
703 	/*
704 	 * Search backwards for beginning of the word.
705 	 */
706 	if (cp == cmdbuf)
707 		return (NULL);
708 	for (word = cp-1;  word > cmdbuf;  word--)
709 		if (word[-1] == ' ')
710 			break;
711 	return (word);
712 }
713 
714 /*
715  * Set things up to enter completion mode.
716  * Expand the word under the cursor into a list of filenames
717  * which start with that word, and set tk_text to that list.
718  */
719 	static void
720 init_compl()
721 {
722 	char *word;
723 	char c;
724 
725 	/*
726 	 * Get rid of any previous tk_text.
727 	 */
728 	if (tk_text != NULL)
729 	{
730 		free(tk_text);
731 		tk_text = NULL;
732 	}
733 	/*
734 	 * Find the original (uncompleted) word in the command buffer.
735 	 */
736 	word = delimit_word();
737 	if (word == NULL)
738 		return;
739 	/*
740 	 * Set the insertion point to the point in the command buffer
741 	 * where the original (uncompleted) word now sits.
742 	 */
743 	tk_ipoint = word;
744 	/*
745 	 * Save the original (uncompleted) word
746 	 */
747 	if (tk_original != NULL)
748 		free(tk_original);
749 	tk_original = (char *) ecalloc(cp-word+1, sizeof(char));
750 	strncpy(tk_original, word, cp-word);
751 	/*
752 	 * Get the expanded filename.
753 	 * This may result in a single filename, or
754 	 * a blank-separated list of filenames.
755 	 */
756 	c = *cp;
757 	*cp = '\0';
758 	tk_text = fcomplete(word);
759 	*cp = c;
760 }
761 
762 /*
763  * Return the next word in the current completion list.
764  */
765 	static char *
766 next_compl(action, prev)
767      	int action;
768 	char *prev;
769 {
770 	switch (action)
771 	{
772 	case EC_F_COMPLETE:
773 		return (forw_textlist(&tk_tlist, prev));
774 	case EC_B_COMPLETE:
775 		return (back_textlist(&tk_tlist, prev));
776 	default:
777 		/* Cannot happen */
778 		return ("?");
779 	}
780 }
781 
782 /*
783  * Complete the filename before (or under) the cursor.
784  * cmd_complete may be called multiple times.  The global in_completion
785  * remembers whether this call is the first time (create the list),
786  * or a subsequent time (step thru the list).
787  */
788 	static int
789 cmd_complete(action)
790 	int action;
791 {
792 
793 	if (!in_completion || action == EC_EXPAND)
794 	{
795 		/*
796 		 * Expand the word under the cursor and
797 		 * use the first word in the expansion
798 		 * (or the entire expansion if we're doing EC_EXPAND).
799 		 */
800 		init_compl();
801 		if (tk_text == NULL)
802 		{
803 			bell();
804 			return (CC_OK);
805 		}
806 		if (action == EC_EXPAND)
807 		{
808 			/*
809 			 * Use the whole list.
810 			 */
811 			tk_trial = tk_text;
812 		} else
813 		{
814 			/*
815 			 * Use the first filename in the list.
816 			 */
817 			in_completion = 1;
818 			init_textlist(&tk_tlist, tk_text);
819 			tk_trial = next_compl(action, (char*)NULL);
820 		}
821 	} else
822 	{
823 		/*
824 		 * We already have a completion list.
825 		 * Use the next/previous filename from the list.
826 		 */
827 		tk_trial = next_compl(action, tk_trial);
828 	}
829 
830   	/*
831   	 * Remove the original word, or the previous trial completion.
832   	 */
833 	while (cp > tk_ipoint)
834 		(void) cmd_erase();
835 
836 	if (tk_trial == NULL)
837 	{
838 		/*
839 		 * There are no more trial completions.
840 		 * Insert the original (uncompleted) filename.
841 		 */
842 		in_completion = 0;
843 		if (cmd_istr(tk_original) != CC_OK)
844 			goto fail;
845 	} else
846 	{
847 		/*
848 		 * Insert trial completion.
849 		 */
850 		if (cmd_istr(tk_trial) != CC_OK)
851 			goto fail;
852 	}
853 
854 	return (CC_OK);
855 
856 fail:
857 	in_completion = 0;
858 	bell();
859 	return (CC_OK);
860 }
861 
862 #endif /* TAB_COMPLETE_FILENAME */
863 
864 /*
865  * Process a single character of a multi-character command, such as
866  * a number, or the pattern of a search command.
867  * Returns:
868  *	CC_OK		The char was accepted.
869  *	CC_QUIT		The char requests the command to be aborted.
870  *	CC_ERROR	The char could not be accepted due to an error.
871  */
872 	public int
873 cmd_char(c)
874 	int c;
875 {
876 	int action;
877 
878 	if (literal)
879 	{
880 		/*
881 		 * Insert the char, even if it is a line-editing char.
882 		 */
883 		literal = 0;
884 		return (cmd_ichar(c));
885 	}
886 
887 	/*
888 	 * See if it is a special line-editing character.
889 	 */
890 	if (in_mca())
891 	{
892 		action = cmd_edit(c);
893 		switch (action)
894 		{
895 		case CC_OK:
896 		case CC_QUIT:
897 			return (action);
898 		case CC_PASS:
899 			break;
900 		}
901 	}
902 
903 	/*
904 	 * Insert the char into the command buffer.
905 	 */
906 	action = cmd_ichar(c);
907 	if (action != CC_OK)
908 		return (action);
909 	return (CC_OK);
910 }
911 
912 /*
913  * Return the number currently in the command buffer.
914  */
915 	public int
916 cmd_int()
917 {
918 	return (atoi(cmdbuf));
919 }
920 
921 /*
922  * Display a string, usually as a prompt for input into the command buffer.
923  */
924 	public void
925 cmd_putstr(s)
926 	char *s;
927 {
928 	putstr(s);
929 	cmd_col += strlen(s);
930 }
931 
932 /*
933  * Return a pointer to the command buffer.
934  */
935 	public char *
936 get_cmdbuf()
937 {
938 	return (cmdbuf);
939 }
940