Merge from vendor branch NTPD:
[dragonfly.git] / contrib / less-381 / cmdbuf.c
1 /*
2  * Copyright (C) 1984-2002  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
20 extern int sc_width;
21
22 static char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */
23 static int cmd_col;             /* Current column of the cursor */
24 static int prompt_col;          /* Column of cursor just after prompt */
25 static char *cp;                /* Pointer into cmdbuf */
26 static int cmd_offset;          /* Index into cmdbuf of first displayed char */
27 static int literal;             /* Next input char should not be interpreted */
28
29 #if TAB_COMPLETE_FILENAME
30 static int cmd_complete();
31 /*
32  * These variables are statics used by cmd_complete.
33  */
34 static int in_completion = 0;
35 static char *tk_text;
36 static char *tk_original;
37 static char *tk_ipoint;
38 static char *tk_trial;
39 static struct textlist tk_tlist;
40 #endif
41
42 static int cmd_left();
43 static int cmd_right();
44
45 #if SPACES_IN_FILENAMES
46 public char openquote = '"';
47 public char closequote = '"';
48 #endif
49
50 #if CMD_HISTORY
51 /*
52  * A mlist structure represents a command history.
53  */
54 struct 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  */
65 struct mlist mlist_search =  
66         { &mlist_search,  &mlist_search,  &mlist_search,  NULL };
67 public void * constant ml_search = (void *) &mlist_search;
68
69 struct mlist mlist_examine = 
70         { &mlist_examine, &mlist_examine, &mlist_examine, NULL };
71 public void * constant ml_examine = (void *) &mlist_examine;
72
73 #if SHELL_ESCAPE || PIPEC
74 struct mlist mlist_shell =   
75         { &mlist_shell,   &mlist_shell,   &mlist_shell,   NULL };
76 public 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. */
82 public void * constant ml_search = (void *)1;
83 public void * constant ml_examine = (void *)2;
84 #if SHELL_ESCAPE || PIPEC
85 public void * constant ml_shell = (void *)3;
86 #endif
87
88 #endif /* CMD_HISTORY */
89
90 /*
91  * History for the current command.
92  */
93 static struct mlist *curr_mlist = NULL;
94 static int curr_cmdflags;
95
96
97 /*
98  * Reset command buffer (to empty).
99  */
100         public void
101 cmd_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
114 clear_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
124 cmd_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
136 len_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
146 cmd_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
176 cmd_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
191 cmd_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
216 cmd_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
246 cmd_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
272 cmd_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
295 cmd_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
328 cmd_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
367 cmd_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
388 cmd_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
414 cmd_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
440 cmd_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
467 set_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
480 cmd_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
522 cmd_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
570 cmd_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
591 cmd_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
703 cmd_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 *
728 delimit_word()
729 {
730         char *word;
731 #if SPACES_IN_FILENAMES
732         char *p;
733         int delim_quoted = 0;
734         int meta_quoted = 0;
735         char *esc = get_meta_escape();
736         int esclen = strlen(esc);
737 #endif
738         
739         /*
740          * Move cursor to end of word.
741          */
742         if (*cp != ' ' && *cp != '\0')
743         {
744                 /*
745                  * Cursor is on a nonspace.
746                  * Move cursor right to the next space.
747                  */
748                 while (*cp != ' ' && *cp != '\0')
749                         cmd_right();
750         } else if (cp > cmdbuf && cp[-1] != ' ')
751         {
752                 /*
753                  * Cursor is on a space, and char to the left is a nonspace.
754                  * We're already at the end of the word.
755                  */
756                 ;
757 #if 0
758         } else
759         {
760                 /*
761                  * Cursor is on a space and char to the left is a space.
762                  * Huh? There's no word here.
763                  */
764                 return (NULL);
765 #endif
766         }
767         /*
768          * Find the beginning of the word which the cursor is in.
769          */
770         if (cp == cmdbuf)
771                 return (NULL);
772 #if SPACES_IN_FILENAMES
773         /*
774          * If we have an unbalanced quote (that is, an open quote
775          * without a corresponding close quote), we return everything
776          * from the open quote, including spaces.
777          */
778         for (word = cmdbuf;  word < cp;  word++)
779                 if (*word != ' ')
780                         break;
781         if (word >= cp)
782                 return (cp);
783         for (p = cmdbuf;  p < cp;  p++)
784         {
785                 if (meta_quoted)
786                 {
787                         meta_quoted = 0;
788                 } else if (esclen > 0 && p + esclen < cp &&
789                            strncmp(p, esc, esclen) == 0)
790                 {
791                         meta_quoted = 1;
792                         p += esclen - 1;
793                 } else if (delim_quoted)
794                 {
795                         if (*p == closequote)
796                                 delim_quoted = 0;
797                 } else /* (!delim_quoted) */
798                 {
799                         if (*p == openquote)
800                                 delim_quoted = 1;
801                         else if (*p == ' ')
802                                 word = p+1;
803                 }
804         }
805 #endif
806         return (word);
807 }
808
809 /*
810  * Set things up to enter completion mode.
811  * Expand the word under the cursor into a list of filenames 
812  * which start with that word, and set tk_text to that list.
813  */
814         static void
815 init_compl()
816 {
817         char *word;
818         char c;
819         
820         /*
821          * Get rid of any previous tk_text.
822          */
823         if (tk_text != NULL)
824         {
825                 free(tk_text);
826                 tk_text = NULL;
827         }
828         /*
829          * Find the original (uncompleted) word in the command buffer.
830          */
831         word = delimit_word();
832         if (word == NULL)
833                 return;
834         /*
835          * Set the insertion point to the point in the command buffer
836          * where the original (uncompleted) word now sits.
837          */
838         tk_ipoint = word;
839         /*
840          * Save the original (uncompleted) word
841          */
842         if (tk_original != NULL)
843                 free(tk_original);
844         tk_original = (char *) ecalloc(cp-word+1, sizeof(char));
845         strncpy(tk_original, word, cp-word);
846         /*
847          * Get the expanded filename.
848          * This may result in a single filename, or
849          * a blank-separated list of filenames.
850          */
851         c = *cp;
852         *cp = '\0';
853         if (*word != openquote)
854         {
855                 tk_text = fcomplete(word);
856         } else
857         {
858                 char *qword = shell_quote(word+1);
859                 if (qword == NULL)
860                         tk_text = fcomplete(word+1);
861                 else
862                 {
863                         tk_text = fcomplete(qword);
864                         free(qword);
865                 }
866         }
867         *cp = c;
868 }
869
870 /*
871  * Return the next word in the current completion list.
872  */
873         static char *
874 next_compl(action, prev)
875         int action;
876         char *prev;
877 {
878         switch (action)
879         {
880         case EC_F_COMPLETE:
881                 return (forw_textlist(&tk_tlist, prev));
882         case EC_B_COMPLETE:
883                 return (back_textlist(&tk_tlist, prev));
884         }
885         /* Cannot happen */
886         return ("?");
887 }
888
889 /*
890  * Complete the filename before (or under) the cursor.
891  * cmd_complete may be called multiple times.  The global in_completion
892  * remembers whether this call is the first time (create the list),
893  * or a subsequent time (step thru the list).
894  */
895         static int
896 cmd_complete(action)
897         int action;
898 {
899         char *s;
900
901         if (!in_completion || action == EC_EXPAND)
902         {
903                 /*
904                  * Expand the word under the cursor and 
905                  * use the first word in the expansion 
906                  * (or the entire expansion if we're doing EC_EXPAND).
907                  */
908                 init_compl();
909                 if (tk_text == NULL)
910                 {
911                         bell();
912                         return (CC_OK);
913                 }
914                 if (action == EC_EXPAND)
915                 {
916                         /*
917                          * Use the whole list.
918                          */
919                         tk_trial = tk_text;
920                 } else
921                 {
922                         /*
923                          * Use the first filename in the list.
924                          */
925                         in_completion = 1;
926                         init_textlist(&tk_tlist, tk_text);
927                         tk_trial = next_compl(action, (char*)NULL);
928                 }
929         } else
930         {
931                 /*
932                  * We already have a completion list.
933                  * Use the next/previous filename from the list.
934                  */
935                 tk_trial = next_compl(action, tk_trial);
936         }
937         
938         /*
939          * Remove the original word, or the previous trial completion.
940          */
941         while (cp > tk_ipoint)
942                 (void) cmd_erase();
943         
944         if (tk_trial == NULL)
945         {
946                 /*
947                  * There are no more trial completions.
948                  * Insert the original (uncompleted) filename.
949                  */
950                 in_completion = 0;
951                 if (cmd_istr(tk_original) != CC_OK)
952                         goto fail;
953         } else
954         {
955                 /*
956                  * Insert trial completion.
957                  */
958                 if (cmd_istr(tk_trial) != CC_OK)
959                         goto fail;
960                 /*
961                  * If it is a directory, append a slash.
962                  */
963                 if (is_dir(tk_trial))
964                 {
965                         if (cp > cmdbuf && cp[-1] == closequote)
966                                 (void) cmd_erase();
967                         s = lgetenv("LESSSEPARATOR");
968                         if (s == NULL)
969                                 s = PATHNAME_SEP;
970                         if (cmd_istr(s) != CC_OK)
971                                 goto fail;
972                 }
973         }
974         
975         return (CC_OK);
976         
977 fail:
978         in_completion = 0;
979         bell();
980         return (CC_OK);
981 }
982
983 #endif /* TAB_COMPLETE_FILENAME */
984
985 /*
986  * Process a single character of a multi-character command, such as
987  * a number, or the pattern of a search command.
988  * Returns:
989  *      CC_OK           The char was accepted.
990  *      CC_QUIT         The char requests the command to be aborted.
991  *      CC_ERROR        The char could not be accepted due to an error.
992  */
993         public int
994 cmd_char(c)
995         int c;
996 {
997         int action;
998
999         if (literal)
1000         {
1001                 /*
1002                  * Insert the char, even if it is a line-editing char.
1003                  */
1004                 literal = 0;
1005                 return (cmd_ichar(c));
1006         }
1007                 
1008         /*
1009          * See if it is a special line-editing character.
1010          */
1011         if (in_mca())
1012         {
1013                 action = cmd_edit(c);
1014                 switch (action)
1015                 {
1016                 case CC_OK:
1017                 case CC_QUIT:
1018                         return (action);
1019                 case CC_PASS:
1020                         break;
1021                 }
1022         }
1023         
1024         /*
1025          * Insert the char into the command buffer.
1026          */
1027         return (cmd_ichar(c));
1028 }
1029
1030 /*
1031  * Return the number currently in the command buffer.
1032  */
1033         public LINENUM
1034 cmd_int()
1035 {
1036         register char *p;
1037         LINENUM n = 0;
1038
1039         for (p = cmdbuf;  *p != '\0';  p++)
1040                 n = (10 * n) + (*p - '0');
1041         return (n);
1042 }
1043
1044 /*
1045  * Return a pointer to the command buffer.
1046  */
1047         public char *
1048 get_cmdbuf()
1049 {
1050         return (cmdbuf);
1051 }