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