]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/less/command.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / less / command.c
1 /* $FreeBSD$ */
2 /*
3  * Copyright (C) 1984-2012  Mark Nudelman
4  *
5  * You may distribute under the terms of either the GNU General Public
6  * License or the Less License, as specified in the README file.
7  *
8  * For more information, see the README file.
9  */
10
11
12 /*
13  * User-level command processor.
14  */
15
16 #include "less.h"
17 #if MSDOS_COMPILER==WIN32C
18 #include <windows.h>
19 #endif
20 #include "position.h"
21 #include "option.h"
22 #include "cmd.h"
23
24 extern int erase_char, erase2_char, kill_char;
25 extern int sigs;
26 extern int quit_if_one_screen;
27 extern int squished;
28 extern int sc_width;
29 extern int sc_height;
30 extern int swindow;
31 extern int jump_sline;
32 extern int quitting;
33 extern int wscroll;
34 extern int top_scroll;
35 extern int ignore_eoi;
36 extern int secure;
37 extern int hshift;
38 extern int show_attn;
39 extern int less_is_more;
40 extern POSITION highest_hilite;
41 extern char *every_first_cmd;
42 extern char *curr_altfilename;
43 extern char version[];
44 extern struct scrpos initial_scrpos;
45 extern IFILE curr_ifile;
46 extern void constant *ml_search;
47 extern void constant *ml_examine;
48 #if SHELL_ESCAPE || PIPEC
49 extern void constant *ml_shell;
50 #endif
51 #if EDITOR
52 extern char *editor;
53 extern char *editproto;
54 #endif
55 extern int screen_trashed;      /* The screen has been overwritten */
56 extern int shift_count;
57 extern int oldbot;
58 extern int forw_prompt;
59
60 #if SHELL_ESCAPE
61 static char *shellcmd = NULL;   /* For holding last shell command for "!!" */
62 #endif
63 static int mca;                 /* The multicharacter command (action) */
64 static int search_type;         /* The previous type of search */
65 static LINENUM number;          /* The number typed by the user */
66 static long fraction;           /* The fractional part of the number */
67 static struct loption *curropt;
68 static int opt_lower;
69 static int optflag;
70 static int optgetname;
71 static POSITION bottompos;
72 static int save_hshift;
73 #if PIPEC
74 static char pipec;
75 #endif
76
77 struct ungot {
78         struct ungot *ug_next;
79         char ug_char;
80 };
81 static struct ungot* ungot = NULL;
82 static int unget_end = 0;
83
84 static void multi_search();
85
86 /*
87  * Move the cursor to start of prompt line before executing a command.
88  * This looks nicer if the command takes a long time before
89  * updating the screen.
90  */
91         static void
92 cmd_exec()
93 {
94 #if HILITE_SEARCH
95         clear_attn();
96 #endif
97         clear_bot();
98         flush();
99 }
100
101 /*
102  * Set up the display to start a new multi-character command.
103  */
104         static void
105 start_mca(action, prompt, mlist, cmdflags)
106         int action;
107         constant char *prompt;
108         constant void *mlist;
109         int cmdflags;
110 {
111         mca = action;
112         clear_bot();
113         clear_cmd();
114         cmd_putstr(prompt);
115         set_mlist(mlist, cmdflags);
116 }
117
118         public int
119 in_mca()
120 {
121         return (mca != 0 && mca != A_PREFIX);
122 }
123
124 /*
125  * Set up the display to start a new search command.
126  */
127         static void
128 mca_search()
129 {
130 #if HILITE_SEARCH
131         if (search_type & SRCH_FILTER)
132                 mca = A_FILTER;
133         else 
134 #endif
135         if (search_type & SRCH_FORW)
136                 mca = A_F_SEARCH;
137         else
138                 mca = A_B_SEARCH;
139
140         clear_bot();
141         clear_cmd();
142
143         if (search_type & SRCH_NO_MATCH)
144                 cmd_putstr("Non-match ");
145         if (search_type & SRCH_FIRST_FILE)
146                 cmd_putstr("First-file ");
147         if (search_type & SRCH_PAST_EOF)
148                 cmd_putstr("EOF-ignore ");
149         if (search_type & SRCH_NO_MOVE)
150                 cmd_putstr("Keep-pos ");
151         if (search_type & SRCH_NO_REGEX)
152                 cmd_putstr("Regex-off ");
153
154 #if HILITE_SEARCH
155         if (search_type & SRCH_FILTER)
156                 cmd_putstr("&/");
157         else 
158 #endif
159         if (search_type & SRCH_FORW)
160                 cmd_putstr("/");
161         else
162                 cmd_putstr("?");
163         set_mlist(ml_search, 0);
164 }
165
166 /*
167  * Set up the display to start a new toggle-option command.
168  */
169         static void
170 mca_opt_toggle()
171 {
172         int no_prompt;
173         int flag;
174         char *dash;
175         
176         no_prompt = (optflag & OPT_NO_PROMPT);
177         flag = (optflag & ~OPT_NO_PROMPT);
178         dash = (flag == OPT_NO_TOGGLE) ? "_" : "-";
179
180         mca = A_OPT_TOGGLE;
181         clear_bot();
182         clear_cmd();
183         cmd_putstr(dash);
184         if (optgetname)
185                 cmd_putstr(dash);
186         if (no_prompt)
187                 cmd_putstr("(P)");
188         switch (flag)
189         {
190         case OPT_UNSET:
191                 cmd_putstr("+");
192                 break;
193         case OPT_SET:
194                 cmd_putstr("!");
195                 break;
196         }
197         set_mlist(NULL, 0);
198 }
199
200 /*
201  * Execute a multicharacter command.
202  */
203         static void
204 exec_mca()
205 {
206         register char *cbuf;
207
208         cmd_exec();
209         cbuf = get_cmdbuf();
210
211         switch (mca)
212         {
213         case A_F_SEARCH:
214         case A_B_SEARCH:
215                 multi_search(cbuf, (int) number);
216                 break;
217 #if HILITE_SEARCH
218         case A_FILTER:
219                 search_type ^= SRCH_NO_MATCH;
220                 set_filter_pattern(cbuf, search_type);
221                 break;
222 #endif
223         case A_FIRSTCMD:
224                 /*
225                  * Skip leading spaces or + signs in the string.
226                  */
227                 while (*cbuf == '+' || *cbuf == ' ')
228                         cbuf++;
229                 if (every_first_cmd != NULL)
230                         free(every_first_cmd);
231                 if (*cbuf == '\0')
232                         every_first_cmd = NULL;
233                 else
234                         every_first_cmd = save(cbuf);
235                 break;
236         case A_OPT_TOGGLE:
237                 toggle_option(curropt, opt_lower, cbuf, optflag);
238                 curropt = NULL;
239                 break;
240         case A_F_BRACKET:
241                 match_brac(cbuf[0], cbuf[1], 1, (int) number);
242                 break;
243         case A_B_BRACKET:
244                 match_brac(cbuf[1], cbuf[0], 0, (int) number);
245                 break;
246 #if EXAMINE
247         case A_EXAMINE:
248                 if (secure)
249                         break;
250                 edit_list(cbuf);
251 #if TAGS
252                 /* If tag structure is loaded then clean it up. */
253                 cleantags();
254 #endif
255                 break;
256 #endif
257 #if SHELL_ESCAPE
258         case A_SHELL:
259                 /*
260                  * !! just uses whatever is in shellcmd.
261                  * Otherwise, copy cmdbuf to shellcmd,
262                  * expanding any special characters ("%" or "#").
263                  */
264                 if (*cbuf != '!')
265                 {
266                         if (shellcmd != NULL)
267                                 free(shellcmd);
268                         shellcmd = fexpand(cbuf);
269                 }
270
271                 if (secure)
272                         break;
273                 if (shellcmd == NULL)
274                         lsystem("", "!done");
275                 else
276                         lsystem(shellcmd, "!done");
277                 break;
278 #endif
279 #if PIPEC
280         case A_PIPE:
281                 if (secure)
282                         break;
283                 (void) pipe_mark(pipec, cbuf);
284                 error("|done", NULL_PARG);
285                 break;
286 #endif
287         }
288 }
289
290 /*
291  * Is a character an erase or kill char?
292  */
293         static int
294 is_erase_char(c)
295         int c;
296 {
297         return (c == erase_char || c == erase2_char || c == kill_char);
298 }
299
300 /*
301  * Handle the first char of an option (after the initial dash).
302  */
303         static int
304 mca_opt_first_char(c)
305     int c;
306 {
307         int flag = (optflag & ~OPT_NO_PROMPT);
308         if (flag == OPT_NO_TOGGLE)
309         {
310                 switch (c)
311                 {
312                 case '_':
313                         /* "__" = long option name. */
314                         optgetname = TRUE;
315                         mca_opt_toggle();
316                         return (MCA_MORE);
317                 }
318         } else
319         {
320                 switch (c)
321                 {
322                 case '+':
323                         /* "-+" = UNSET. */
324                         optflag = (flag == OPT_UNSET) ?
325                                 OPT_TOGGLE : OPT_UNSET;
326                         mca_opt_toggle();
327                         return (MCA_MORE);
328                 case '!':
329                         /* "-!" = SET */
330                         optflag = (flag == OPT_SET) ?
331                                 OPT_TOGGLE : OPT_SET;
332                         mca_opt_toggle();
333                         return (MCA_MORE);
334                 case CONTROL('P'):
335                         optflag ^= OPT_NO_PROMPT;
336                         mca_opt_toggle();
337                         return (MCA_MORE);
338                 case '-':
339                         /* "--" = long option name. */
340                         optgetname = TRUE;
341                         mca_opt_toggle();
342                         return (MCA_MORE);
343                 }
344         }
345         /* Char was not handled here. */
346         return (NO_MCA);
347 }
348
349 /*
350  * Add a char to a long option name.
351  * See if we've got a match for an option name yet.
352  * If so, display the complete name and stop 
353  * accepting chars until user hits RETURN.
354  */
355         static int
356 mca_opt_nonfirst_char(c)
357         int c;
358 {
359         char *p;
360         char *oname;
361
362         if (curropt != NULL)
363         {
364                 /*
365                  * Already have a match for the name.
366                  * Don't accept anything but erase/kill.
367                  */
368                 if (is_erase_char(c))
369                         return (MCA_DONE);
370                 return (MCA_MORE);
371         }
372         /*
373          * Add char to cmd buffer and try to match
374          * the option name.
375          */
376         if (cmd_char(c) == CC_QUIT)
377                 return (MCA_DONE);
378         p = get_cmdbuf();
379         opt_lower = ASCII_IS_LOWER(p[0]);
380         curropt = findopt_name(&p, &oname, NULL);
381         if (curropt != NULL)
382         {
383                 /*
384                  * Got a match.
385                  * Remember the option and
386                  * display the full option name.
387                  */
388                 cmd_reset();
389                 mca_opt_toggle();
390                 for (p = oname;  *p != '\0';  p++)
391                 {
392                         c = *p;
393                         if (!opt_lower && ASCII_IS_LOWER(c))
394                                 c = ASCII_TO_UPPER(c);
395                         if (cmd_char(c) != CC_OK)
396                                 return (MCA_DONE);
397                 }
398         }
399         return (MCA_MORE);
400 }
401
402 /*
403  * Handle a char of an option toggle command.
404  */
405         static int
406 mca_opt_char(c)
407         int c;
408 {
409         PARG parg;
410
411         /*
412          * This may be a short option (single char),
413          * or one char of a long option name,
414          * or one char of the option parameter.
415          */
416         if (curropt == NULL && len_cmdbuf() == 0)
417         {
418                 int ret = mca_opt_first_char(c);
419                 if (ret != NO_MCA)
420                         return (ret);
421         }
422         if (optgetname)
423         {
424                 /* We're getting a long option name.  */
425                 if (c != '\n' && c != '\r')
426                         return (mca_opt_nonfirst_char(c));
427                 if (curropt == NULL)
428                 {
429                         parg.p_string = get_cmdbuf();
430                         error("There is no --%s option", &parg);
431                         return (MCA_DONE);
432                 }
433                 optgetname = FALSE;
434                 cmd_reset();
435         } else
436         {
437                 if (is_erase_char(c))
438                         return (NO_MCA);
439                 if (curropt != NULL)
440                         /* We're getting the option parameter. */
441                         return (NO_MCA);
442                 curropt = findopt(c);
443                 if (curropt == NULL)
444                 {
445                         parg.p_string = propt(c);
446                         error("There is no %s option", &parg);
447                         return (MCA_DONE);
448                 }
449         }
450         /*
451          * If the option which was entered does not take a 
452          * parameter, toggle the option immediately,
453          * so user doesn't have to hit RETURN.
454          */
455         if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE ||
456             !opt_has_param(curropt))
457         {
458                 toggle_option(curropt, ASCII_IS_LOWER(c), "", optflag);
459                 return (MCA_DONE);
460         }
461         /*
462          * Display a prompt appropriate for the option parameter.
463          */
464         start_mca(A_OPT_TOGGLE, opt_prompt(curropt), (void*)NULL, 0);
465         return (MCA_MORE);
466 }
467
468 /*
469  * Handle a char of a search command.
470  */
471         static int
472 mca_search_char(c)
473         int c;
474 {
475         int flag = 0;
476
477         /*
478          * Certain characters as the first char of 
479          * the pattern have special meaning:
480          *      !  Toggle the NO_MATCH flag
481          *      *  Toggle the PAST_EOF flag
482          *      @  Toggle the FIRST_FILE flag
483          */
484         if (len_cmdbuf() > 0)
485                 return (NO_MCA);
486
487         switch (c)
488         {
489         case '*':
490                 if (less_is_more)
491                         break;
492         case CONTROL('E'): /* ignore END of file */
493                 if (mca != A_FILTER)
494                         flag = SRCH_PAST_EOF;
495                 break;
496         case '@':
497                 if (less_is_more)
498                         break;
499         case CONTROL('F'): /* FIRST file */
500                 if (mca != A_FILTER)
501                         flag = SRCH_FIRST_FILE;
502                 break;
503         case CONTROL('K'): /* KEEP position */
504                 if (mca != A_FILTER)
505                         flag = SRCH_NO_MOVE;
506                 break;
507         case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */
508                 flag = SRCH_NO_REGEX;
509                 break;
510         case CONTROL('N'): /* NOT match */
511         case '!':
512                 flag = SRCH_NO_MATCH;
513                 break;
514         }
515
516         if (flag != 0)
517         {
518                 search_type ^= flag;
519                 mca_search();
520                 return (MCA_MORE);
521         }
522         return (NO_MCA);
523 }
524
525 /*
526  * Handle a character of a multi-character command.
527  */
528         static int
529 mca_char(c)
530         int c;
531 {
532         int ret;
533
534         switch (mca)
535         {
536         case 0:
537                 /*
538                  * We're not in a multicharacter command.
539                  */
540                 return (NO_MCA);
541
542         case A_PREFIX:
543                 /*
544                  * In the prefix of a command.
545                  * This not considered a multichar command
546                  * (even tho it uses cmdbuf, etc.).
547                  * It is handled in the commands() switch.
548                  */
549                 return (NO_MCA);
550
551         case A_DIGIT:
552                 /*
553                  * Entering digits of a number.
554                  * Terminated by a non-digit.
555                  */
556                 if (!((c >= '0' && c <= '9') || c == '.') && 
557                   editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE|EC_NORIGHTLEFT) == A_INVALID)
558                 {
559                         /*
560                          * Not part of the number.
561                          * End the number and treat this char 
562                          * as a normal command character.
563                          */
564                         number = cmd_int(&fraction);
565                         mca = 0;
566                         cmd_accept();
567                         return (NO_MCA);
568                 }
569                 break;
570
571         case A_OPT_TOGGLE:
572                 ret = mca_opt_char(c);
573                 if (ret != NO_MCA)
574                         return (ret);
575                 break;
576
577         case A_F_SEARCH:
578         case A_B_SEARCH:
579         case A_FILTER:
580                 ret = mca_search_char(c);
581                 if (ret != NO_MCA)
582                         return (ret);
583                 break;
584
585         default:
586                 /* Other multicharacter command. */
587                 break;
588         }
589
590         /*
591          * The multichar command is terminated by a newline.
592          */
593         if (c == '\n' || c == '\r')
594         {
595                 /*
596                  * Execute the command.
597                  */
598                 exec_mca();
599                 return (MCA_DONE);
600         }
601
602         /*
603          * Append the char to the command buffer.
604          */
605         if (cmd_char(c) == CC_QUIT)
606                 /*
607                  * Abort the multi-char command.
608                  */
609                 return (MCA_DONE);
610
611         if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
612         {
613                 /*
614                  * Special case for the bracket-matching commands.
615                  * Execute the command after getting exactly two
616                  * characters from the user.
617                  */
618                 exec_mca();
619                 return (MCA_DONE);
620         }
621
622         /*
623          * Need another character.
624          */
625         return (MCA_MORE);
626 }
627
628 /*
629  * Discard any buffered file data.
630  */
631         static void
632 clear_buffers()
633 {
634         if (!(ch_getflags() & CH_CANSEEK))
635                 return;
636         ch_flush();
637         clr_linenum();
638 #if HILITE_SEARCH
639         clr_hilite();
640 #endif
641 }
642
643 /*
644  * Make sure the screen is displayed.
645  */
646         static void
647 make_display()
648 {
649         /*
650          * If nothing is displayed yet, display starting from initial_scrpos.
651          */
652         if (empty_screen())
653         {
654                 if (initial_scrpos.pos == NULL_POSITION)
655                         /*
656                          * {{ Maybe this should be:
657                          *    jump_loc(ch_zero(), jump_sline);
658                          *    but this behavior seems rather unexpected 
659                          *    on the first screen. }}
660                          */
661                         jump_loc(ch_zero(), 1);
662                 else
663                         jump_loc(initial_scrpos.pos, initial_scrpos.ln);
664         } else if (screen_trashed)
665         {
666                 int save_top_scroll = top_scroll;
667                 int save_ignore_eoi = ignore_eoi;
668                 top_scroll = 1;
669                 ignore_eoi = 0;
670                 if (screen_trashed == 2)
671                 {
672                         /* Special case used by ignore_eoi: re-open the input file
673                          * and jump to the end of the file. */
674                         reopen_curr_ifile();
675                         jump_forw();
676                 }
677                 repaint();
678                 top_scroll = save_top_scroll;
679                 ignore_eoi = save_ignore_eoi;
680         }
681 }
682
683 /*
684  * Display the appropriate prompt.
685  */
686         static void
687 prompt()
688 {
689         register constant char *p;
690
691         if (ungot != NULL)
692         {
693                 /*
694                  * No prompt necessary if commands are from 
695                  * ungotten chars rather than from the user.
696                  */
697                 return;
698         }
699
700         /*
701          * Make sure the screen is displayed.
702          */
703         make_display();
704         bottompos = position(BOTTOM_PLUS_ONE);
705
706         /*
707          * If we've hit EOF on the last file and the -E flag is set, quit.
708          */
709         if (get_quit_at_eof() == OPT_ONPLUS &&
710             eof_displayed() && !(ch_getflags() & CH_HELPFILE) && 
711             next_ifile(curr_ifile) == NULL_IFILE)
712                 quit(QUIT_OK);
713
714         /*
715          * If the entire file is displayed and the -F flag is set, quit.
716          */
717         if (quit_if_one_screen &&
718             entire_file_displayed() && !(ch_getflags() & CH_HELPFILE) && 
719             next_ifile(curr_ifile) == NULL_IFILE)
720                 quit(QUIT_OK);
721
722 #if MSDOS_COMPILER==WIN32C
723         /* 
724          * In Win32, display the file name in the window title.
725          */
726         if (!(ch_getflags() & CH_HELPFILE))
727                 SetConsoleTitle(pr_expand("Less?f - %f.", 0));
728 #endif
729         /*
730          * Select the proper prompt and display it.
731          */
732         /*
733          * If the previous action was a forward movement, 
734          * don't clear the bottom line of the display;
735          * just print the prompt since the forward movement guarantees 
736          * that we're in the right position to display the prompt.
737          * Clearing the line could cause a problem: for example, if the last
738          * line displayed ended at the right screen edge without a newline,
739          * then clearing would clear the last displayed line rather than
740          * the prompt line.
741          */
742         if (!forw_prompt)
743                 clear_bot();
744         clear_cmd();
745         forw_prompt = 0;
746         p = pr_string();
747         if (is_filtering())
748                 putstr("& ");
749         if (p == NULL || *p == '\0')
750                 putchr(':');
751         else
752         {
753                 at_enter(AT_STANDOUT);
754                 putstr(p);
755                 at_exit();
756         }
757         clear_eol();
758 }
759
760 /*
761  * Display the less version message.
762  */
763         public void
764 dispversion()
765 {
766         PARG parg;
767
768         parg.p_string = version;
769         error("less %s", &parg);
770 }
771
772 /*
773  * Get command character.
774  * The character normally comes from the keyboard,
775  * but may come from ungotten characters
776  * (characters previously given to ungetcc or ungetsc).
777  */
778         public int
779 getcc()
780 {
781         if (unget_end) 
782         {
783                 /*
784                  * We have just run out of ungotten chars.
785                  */
786                 unget_end = 0;
787                 if (len_cmdbuf() == 0 || !empty_screen())
788                         return (getchr());
789                 /*
790                  * Command is incomplete, so try to complete it.
791                  */
792                 switch (mca)
793                 {
794                 case A_DIGIT:
795                         /*
796                          * We have a number but no command.  Treat as #g.
797                          */
798                         return ('g');
799
800                 case A_F_SEARCH:
801                 case A_B_SEARCH:
802                         /*
803                          * We have "/string" but no newline.  Add the \n.
804                          */
805                         return ('\n'); 
806
807                 default:
808                         /*
809                          * Some other incomplete command.  Let user complete it.
810                          */
811                         return (getchr());
812                 }
813         }
814
815         if (ungot == NULL)
816         {
817                 /*
818                  * Normal case: no ungotten chars, so get one from the user.
819                  */
820                 return (getchr());
821         }
822
823         /*
824          * Return the next ungotten char.
825          */
826         {
827                 struct ungot *ug = ungot;
828                 char c = ug->ug_char;
829                 ungot = ug->ug_next;
830                 free(ug);
831                 unget_end = (ungot == NULL);
832                 return (c);
833         }
834 }
835
836 /*
837  * "Unget" a command character.
838  * The next getcc() will return this character.
839  */
840         public void
841 ungetcc(c)
842         int c;
843 {
844         struct ungot *ug = (struct ungot *) ecalloc(1, sizeof(struct ungot));
845
846         ug->ug_char = c;
847         ug->ug_next = ungot;
848         ungot = ug;
849         unget_end = 0;
850 }
851
852 /*
853  * Unget a whole string of command characters.
854  * The next sequence of getcc()'s will return this string.
855  */
856         public void
857 ungetsc(s)
858         char *s;
859 {
860         register char *p;
861
862         for (p = s + strlen(s) - 1;  p >= s;  p--)
863                 ungetcc(*p);
864 }
865
866 /*
867  * Search for a pattern, possibly in multiple files.
868  * If SRCH_FIRST_FILE is set, begin searching at the first file.
869  * If SRCH_PAST_EOF is set, continue the search thru multiple files.
870  */
871         static void
872 multi_search(pattern, n)
873         char *pattern;
874         int n;
875 {
876         register int nomore;
877         IFILE save_ifile;
878         int changed_file;
879
880         changed_file = 0;
881         save_ifile = save_curr_ifile();
882
883         if (search_type & SRCH_FIRST_FILE)
884         {
885                 /*
886                  * Start at the first (or last) file 
887                  * in the command line list.
888                  */
889                 if (search_type & SRCH_FORW)
890                         nomore = edit_first();
891                 else
892                         nomore = edit_last();
893                 if (nomore)
894                 {
895                         unsave_ifile(save_ifile);
896                         return;
897                 }
898                 changed_file = 1;
899                 search_type &= ~SRCH_FIRST_FILE;
900         }
901
902         for (;;)
903         {
904                 n = search(search_type, pattern, n);
905                 /*
906                  * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared
907                  * after being used once.  This allows "n" to work after
908                  * using a /@@ search.
909                  */
910                 search_type &= ~SRCH_NO_MOVE;
911                 if (n == 0)
912                 {
913                         /*
914                          * Found it.
915                          */
916                         unsave_ifile(save_ifile);
917                         return;
918                 }
919
920                 if (n < 0)
921                         /*
922                          * Some kind of error in the search.
923                          * Error message has been printed by search().
924                          */
925                         break;
926
927                 if ((search_type & SRCH_PAST_EOF) == 0)
928                         /*
929                          * We didn't find a match, but we're
930                          * supposed to search only one file.
931                          */
932                         break;
933                 /*
934                  * Move on to the next file.
935                  */
936                 if (search_type & SRCH_FORW)
937                         nomore = edit_next(1);
938                 else
939                         nomore = edit_prev(1);
940                 if (nomore)
941                         break;
942                 changed_file = 1;
943         }
944
945         /*
946          * Didn't find it.
947          * Print an error message if we haven't already.
948          */
949         if (n > 0)
950                 error("Pattern not found", NULL_PARG);
951
952         if (changed_file)
953         {
954                 /*
955                  * Restore the file we were originally viewing.
956                  */
957                 reedit_ifile(save_ifile);
958         } else
959         {
960                 unsave_ifile(save_ifile);
961         }
962 }
963
964 /*
965  * Forward forever, or until a highlighted line appears.
966  */
967         static int
968 forw_loop(until_hilite)
969         int until_hilite;
970 {
971         POSITION curr_len;
972
973         if (ch_getflags() & CH_HELPFILE)
974                 return (A_NOACTION);
975
976         cmd_exec();
977         jump_forw();
978         curr_len = ch_length();
979         highest_hilite = until_hilite ? curr_len : NULL_POSITION;
980         ignore_eoi = 1;
981         while (!sigs)
982         {
983                 if (until_hilite && highest_hilite > curr_len)
984                 {
985                         bell();
986                         break;
987                 }
988                 make_display();
989                 forward(1, 0, 0);
990         }
991         ignore_eoi = 0;
992         ch_set_eof();
993
994         /*
995          * This gets us back in "F mode" after processing 
996          * a non-abort signal (e.g. window-change).  
997          */
998         if (sigs && !ABORT_SIGS())
999                 return (until_hilite ? A_F_UNTIL_HILITE : A_F_FOREVER);
1000
1001         return (A_NOACTION);
1002 }
1003
1004 /*
1005  * Main command processor.
1006  * Accept and execute commands until a quit command.
1007  */
1008         public void
1009 commands()
1010 {
1011         register int c;
1012         register int action;
1013         register char *cbuf;
1014         int newaction;
1015         int save_search_type;
1016         char *extra;
1017         char tbuf[2];
1018         PARG parg;
1019         IFILE old_ifile;
1020         IFILE new_ifile;
1021         char *tagfile;
1022         int until_hilite = 0;
1023
1024         search_type = SRCH_FORW;
1025         wscroll = (sc_height + 1) / 2;
1026         newaction = A_NOACTION;
1027
1028         for (;;)
1029         {
1030                 mca = 0;
1031                 cmd_accept();
1032                 number = 0;
1033                 curropt = NULL;
1034
1035                 /*
1036                  * See if any signals need processing.
1037                  */
1038                 if (sigs)
1039                 {
1040                         psignals();
1041                         if (quitting)
1042                                 quit(QUIT_SAVED_STATUS);
1043                 }
1044
1045                 /*
1046                  * See if window size changed, for systems that don't
1047                  * generate SIGWINCH.
1048                  */
1049                 check_winch();
1050
1051                 /*
1052                  * Display prompt and accept a character.
1053                  */
1054                 cmd_reset();
1055                 prompt();
1056                 if (sigs)
1057                         continue;
1058                 if (newaction == A_NOACTION)
1059                         c = getcc();
1060
1061         again:
1062                 if (sigs)
1063                         continue;
1064
1065                 if (newaction != A_NOACTION)
1066                 {
1067                         action = newaction;
1068                         newaction = A_NOACTION;
1069                 } else
1070                 {
1071                         /*
1072                          * If we are in a multicharacter command, call mca_char.
1073                          * Otherwise we call fcmd_decode to determine the
1074                          * action to be performed.
1075                          */
1076                         if (mca)
1077                                 switch (mca_char(c))
1078                                 {
1079                                 case MCA_MORE:
1080                                         /*
1081                                          * Need another character.
1082                                          */
1083                                         c = getcc();
1084                                         goto again;
1085                                 case MCA_DONE:
1086                                         /*
1087                                          * Command has been handled by mca_char.
1088                                          * Start clean with a prompt.
1089                                          */
1090                                         continue;
1091                                 case NO_MCA:
1092                                         /*
1093                                          * Not a multi-char command
1094                                          * (at least, not anymore).
1095                                          */
1096                                         break;
1097                                 }
1098
1099                         /*
1100                          * Decode the command character and decide what to do.
1101                          */
1102                         if (mca)
1103                         {
1104                                 /*
1105                                  * We're in a multichar command.
1106                                  * Add the character to the command buffer
1107                                  * and display it on the screen.
1108                                  * If the user backspaces past the start 
1109                                  * of the line, abort the command.
1110                                  */
1111                                 if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
1112                                         continue;
1113                                 cbuf = get_cmdbuf();
1114                         } else
1115                         {
1116                                 /*
1117                                  * Don't use cmd_char if we're starting fresh
1118                                  * at the beginning of a command, because we
1119                                  * don't want to echo the command until we know
1120                                  * it is a multichar command.  We also don't
1121                                  * want erase_char/kill_char to be treated
1122                                  * as line editing characters.
1123                                  */
1124                                 tbuf[0] = c;
1125                                 tbuf[1] = '\0';
1126                                 cbuf = tbuf;
1127                         }
1128                         extra = NULL;
1129                         action = fcmd_decode(cbuf, &extra);
1130                         /*
1131                          * If an "extra" string was returned,
1132                          * process it as a string of command characters.
1133                          */
1134                         if (extra != NULL)
1135                                 ungetsc(extra);
1136                 }
1137                 /*
1138                  * Clear the cmdbuf string.
1139                  * (But not if we're in the prefix of a command,
1140                  * because the partial command string is kept there.)
1141                  */
1142                 if (action != A_PREFIX)
1143                         cmd_reset();
1144
1145                 switch (action)
1146                 {
1147                 case A_DIGIT:
1148                         /*
1149                          * First digit of a number.
1150                          */
1151                         start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE);
1152                         goto again;
1153
1154                 case A_F_WINDOW:
1155                         /*
1156                          * Forward one window (and set the window size).
1157                          */
1158                         if (number > 0)
1159                                 swindow = (int) number;
1160                         /* FALLTHRU */
1161                 case A_F_SCREEN:
1162                         /*
1163                          * Forward one screen.
1164                          */
1165                         if (number <= 0)
1166                                 number = get_swindow();
1167                         cmd_exec();
1168                         if (show_attn)
1169                                 set_attnpos(bottompos);
1170                         forward((int) number, 0, 1);
1171                         break;
1172
1173                 case A_B_WINDOW:
1174                         /*
1175                          * Backward one window (and set the window size).
1176                          */
1177                         if (number > 0)
1178                                 swindow = (int) number;
1179                         /* FALLTHRU */
1180                 case A_B_SCREEN:
1181                         /*
1182                          * Backward one screen.
1183                          */
1184                         if (number <= 0)
1185                                 number = get_swindow();
1186                         cmd_exec();
1187                         backward((int) number, 0, 1);
1188                         break;
1189
1190                 case A_F_LINE:
1191                         /*
1192                          * Forward N (default 1) line.
1193                          */
1194                         if (number <= 0)
1195                                 number = 1;
1196                         cmd_exec();
1197                         if (show_attn == OPT_ONPLUS && number > 1)
1198                                 set_attnpos(bottompos);
1199                         forward((int) number, 0, 0);
1200                         break;
1201
1202                 case A_B_LINE:
1203                         /*
1204                          * Backward N (default 1) line.
1205                          */
1206                         if (number <= 0)
1207                                 number = 1;
1208                         cmd_exec();
1209                         backward((int) number, 0, 0);
1210                         break;
1211
1212                 case A_FF_LINE:
1213                         /*
1214                          * Force forward N (default 1) line.
1215                          */
1216                         if (number <= 0)
1217                                 number = 1;
1218                         cmd_exec();
1219                         if (show_attn == OPT_ONPLUS && number > 1)
1220                                 set_attnpos(bottompos);
1221                         forward((int) number, 1, 0);
1222                         break;
1223
1224                 case A_BF_LINE:
1225                         /*
1226                          * Force backward N (default 1) line.
1227                          */
1228                         if (number <= 0)
1229                                 number = 1;
1230                         cmd_exec();
1231                         backward((int) number, 1, 0);
1232                         break;
1233                 
1234                 case A_FF_SCREEN:
1235                         /*
1236                          * Force forward one screen.
1237                          */
1238                         if (number <= 0)
1239                                 number = get_swindow();
1240                         cmd_exec();
1241                         if (show_attn == OPT_ONPLUS)
1242                                 set_attnpos(bottompos);
1243                         forward((int) number, 1, 0);
1244                         break;
1245
1246                 case A_F_FOREVER:
1247                         /*
1248                          * Forward forever, ignoring EOF.
1249                          */
1250                         newaction = forw_loop(0);
1251                         break;
1252
1253                 case A_F_UNTIL_HILITE:
1254                         newaction = forw_loop(1);
1255                         break;
1256
1257                 case A_F_SCROLL:
1258                         /*
1259                          * Forward N lines 
1260                          * (default same as last 'd' or 'u' command).
1261                          */
1262                         if (number > 0)
1263                                 wscroll = (int) number;
1264                         cmd_exec();
1265                         if (show_attn == OPT_ONPLUS)
1266                                 set_attnpos(bottompos);
1267                         forward(wscroll, 0, 0);
1268                         break;
1269
1270                 case A_B_SCROLL:
1271                         /*
1272                          * Forward N lines 
1273                          * (default same as last 'd' or 'u' command).
1274                          */
1275                         if (number > 0)
1276                                 wscroll = (int) number;
1277                         cmd_exec();
1278                         backward(wscroll, 0, 0);
1279                         break;
1280
1281                 case A_FREPAINT:
1282                         /*
1283                          * Flush buffers, then repaint screen.
1284                          * Don't flush the buffers on a pipe!
1285                          */
1286                         clear_buffers();
1287                         /* FALLTHRU */
1288                 case A_REPAINT:
1289                         /*
1290                          * Repaint screen.
1291                          */
1292                         cmd_exec();
1293                         repaint();
1294                         break;
1295
1296                 case A_GOLINE:
1297                         /*
1298                          * Go to line N, default beginning of file.
1299                          */
1300                         if (number <= 0)
1301                                 number = 1;
1302                         cmd_exec();
1303                         jump_back(number);
1304                         break;
1305
1306                 case A_PERCENT:
1307                         /*
1308                          * Go to a specified percentage into the file.
1309                          */
1310                         if (number < 0)
1311                         {
1312                                 number = 0;
1313                                 fraction = 0;
1314                         }
1315                         if (number > 100)
1316                         {
1317                                 number = 100;
1318                                 fraction = 0;
1319                         }
1320                         cmd_exec();
1321                         jump_percent((int) number, fraction);
1322                         break;
1323
1324                 case A_GOEND:
1325                         /*
1326                          * Go to line N, default end of file.
1327                          */
1328                         cmd_exec();
1329                         if (number <= 0)
1330                                 jump_forw();
1331                         else
1332                                 jump_back(number);
1333                         break;
1334
1335                 case A_GOPOS:
1336                         /*
1337                          * Go to a specified byte position in the file.
1338                          */
1339                         cmd_exec();
1340                         if (number < 0)
1341                                 number = 0;
1342                         jump_line_loc((POSITION) number, jump_sline);
1343                         break;
1344
1345                 case A_STAT:
1346                         /*
1347                          * Print file name, etc.
1348                          */
1349                         if (ch_getflags() & CH_HELPFILE)
1350                                 break;
1351                         cmd_exec();
1352                         parg.p_string = eq_message();
1353                         error("%s", &parg);
1354                         break;
1355
1356                 case A_VERSION:
1357                         /*
1358                          * Print version number, without the "@(#)".
1359                          */
1360                         cmd_exec();
1361                         dispversion();
1362                         break;
1363
1364                 case A_QUIT:
1365                         /*
1366                          * Exit.
1367                          */
1368                         if (curr_ifile != NULL_IFILE && 
1369                             ch_getflags() & CH_HELPFILE)
1370                         {
1371                                 /*
1372                                  * Quit while viewing the help file
1373                                  * just means return to viewing the
1374                                  * previous file.
1375                                  */
1376                                 hshift = save_hshift;
1377                                 if (edit_prev(1) == 0)
1378                                         break;
1379                         }
1380                         if (extra != NULL)
1381                                 quit(*extra);
1382                         quit(QUIT_OK);
1383                         break;
1384
1385 /*
1386  * Define abbreviation for a commonly used sequence below.
1387  */
1388 #define DO_SEARCH() \
1389                         if (number <= 0) number = 1;    \
1390                         mca_search();                   \
1391                         cmd_exec();                     \
1392                         multi_search((char *)NULL, (int) number);
1393
1394
1395                 case A_F_SEARCH:
1396                         /*
1397                          * Search forward for a pattern.
1398                          * Get the first char of the pattern.
1399                          */
1400                         search_type = SRCH_FORW;
1401                         if (number <= 0)
1402                                 number = 1;
1403                         mca_search();
1404                         c = getcc();
1405                         goto again;
1406
1407                 case A_B_SEARCH:
1408                         /*
1409                          * Search backward for a pattern.
1410                          * Get the first char of the pattern.
1411                          */
1412                         search_type = SRCH_BACK;
1413                         if (number <= 0)
1414                                 number = 1;
1415                         mca_search();
1416                         c = getcc();
1417                         goto again;
1418
1419                 case A_FILTER:
1420 #if HILITE_SEARCH
1421                         search_type = SRCH_FORW | SRCH_FILTER;
1422                         mca_search();
1423                         c = getcc();
1424                         goto again;
1425 #else
1426                         error("Command not available", NULL_PARG);
1427                         break;
1428 #endif
1429
1430                 case A_AGAIN_SEARCH:
1431                         /*
1432                          * Repeat previous search.
1433                          */
1434                         DO_SEARCH();
1435                         break;
1436                 
1437                 case A_T_AGAIN_SEARCH:
1438                         /*
1439                          * Repeat previous search, multiple files.
1440                          */
1441                         search_type |= SRCH_PAST_EOF;
1442                         DO_SEARCH();
1443                         break;
1444
1445                 case A_REVERSE_SEARCH:
1446                         /*
1447                          * Repeat previous search, in reverse direction.
1448                          */
1449                         save_search_type = search_type;
1450                         search_type = SRCH_REVERSE(search_type);
1451                         DO_SEARCH();
1452                         search_type = save_search_type;
1453                         break;
1454
1455                 case A_T_REVERSE_SEARCH:
1456                         /* 
1457                          * Repeat previous search, 
1458                          * multiple files in reverse direction.
1459                          */
1460                         save_search_type = search_type;
1461                         search_type = SRCH_REVERSE(search_type);
1462                         search_type |= SRCH_PAST_EOF;
1463                         DO_SEARCH();
1464                         search_type = save_search_type;
1465                         break;
1466
1467                 case A_UNDO_SEARCH:
1468                         undo_search();
1469                         break;
1470
1471                 case A_HELP:
1472                         /*
1473                          * Help.
1474                          */
1475                         if (ch_getflags() & CH_HELPFILE)
1476                                 break;
1477                         cmd_exec();
1478                         save_hshift = hshift;
1479                         hshift = 0;
1480                         (void) edit(FAKE_HELPFILE);
1481                         break;
1482
1483                 case A_EXAMINE:
1484 #if EXAMINE
1485                         /*
1486                          * Edit a new file.  Get the filename.
1487                          */
1488                         if (secure)
1489                         {
1490                                 error("Command not available", NULL_PARG);
1491                                 break;
1492                         }
1493                         start_mca(A_EXAMINE, "Examine: ", ml_examine, 0);
1494                         c = getcc();
1495                         goto again;
1496 #else
1497                         error("Command not available", NULL_PARG);
1498                         break;
1499 #endif
1500                         
1501                 case A_VISUAL:
1502                         /*
1503                          * Invoke an editor on the input file.
1504                          */
1505 #if EDITOR
1506                         if (secure)
1507                         {
1508                                 error("Command not available", NULL_PARG);
1509                                 break;
1510                         }
1511                         if (ch_getflags() & CH_HELPFILE)
1512                                 break;
1513                         if (strcmp(get_filename(curr_ifile), "-") == 0)
1514                         {
1515                                 error("Cannot edit standard input", NULL_PARG);
1516                                 break;
1517                         }
1518                         if (curr_altfilename != NULL)
1519                         {
1520                                 error("WARNING: This file was viewed via LESSOPEN",
1521                                         NULL_PARG);
1522                         }
1523                         start_mca(A_SHELL, "!", ml_shell, 0);
1524                         /*
1525                          * Expand the editor prototype string
1526                          * and pass it to the system to execute.
1527                          * (Make sure the screen is displayed so the
1528                          * expansion of "+%lm" works.)
1529                          */
1530                         make_display();
1531                         cmd_exec();
1532                         lsystem(pr_expand(editproto, 0), (char*)NULL);
1533                         break;
1534 #else
1535                         error("Command not available", NULL_PARG);
1536                         break;
1537 #endif
1538
1539                 case A_NEXT_FILE:
1540                         /*
1541                          * Examine next file.
1542                          */
1543 #if TAGS
1544                         if (ntags())
1545                         {
1546                                 error("No next file", NULL_PARG);
1547                                 break;
1548                         }
1549 #endif
1550                         if (number <= 0)
1551                                 number = 1;
1552                         if (edit_next((int) number))
1553                         {
1554                                 if (get_quit_at_eof() && eof_displayed() && 
1555                                     !(ch_getflags() & CH_HELPFILE))
1556                                         quit(QUIT_OK);
1557                                 parg.p_string = (number > 1) ? "(N-th) " : "";
1558                                 error("No %snext file", &parg);
1559                         }
1560                         break;
1561
1562                 case A_PREV_FILE:
1563                         /*
1564                          * Examine previous file.
1565                          */
1566 #if TAGS
1567                         if (ntags())
1568                         {
1569                                 error("No previous file", NULL_PARG);
1570                                 break;
1571                         }
1572 #endif
1573                         if (number <= 0)
1574                                 number = 1;
1575                         if (edit_prev((int) number))
1576                         {
1577                                 parg.p_string = (number > 1) ? "(N-th) " : "";
1578                                 error("No %sprevious file", &parg);
1579                         }
1580                         break;
1581
1582                 case A_NEXT_TAG:
1583 #if TAGS
1584                         if (number <= 0)
1585                                 number = 1;
1586                         tagfile = nexttag((int) number);
1587                         if (tagfile == NULL)
1588                         {
1589                                 error("No next tag", NULL_PARG);
1590                                 break;
1591                         }
1592                         if (edit(tagfile) == 0)
1593                         {
1594                                 POSITION pos = tagsearch();
1595                                 if (pos != NULL_POSITION)
1596                                         jump_loc(pos, jump_sline);
1597                         }
1598 #else
1599                         error("Command not available", NULL_PARG);
1600 #endif
1601                         break;
1602
1603                 case A_PREV_TAG:
1604 #if TAGS
1605                         if (number <= 0)
1606                                 number = 1;
1607                         tagfile = prevtag((int) number);
1608                         if (tagfile == NULL)
1609                         {
1610                                 error("No previous tag", NULL_PARG);
1611                                 break;
1612                         }
1613                         if (edit(tagfile) == 0)
1614                         {
1615                                 POSITION pos = tagsearch();
1616                                 if (pos != NULL_POSITION)
1617                                         jump_loc(pos, jump_sline);
1618                         }
1619 #else
1620                         error("Command not available", NULL_PARG);
1621 #endif
1622                         break;
1623
1624                 case A_INDEX_FILE:
1625                         /*
1626                          * Examine a particular file.
1627                          */
1628                         if (number <= 0)
1629                                 number = 1;
1630                         if (edit_index((int) number))
1631                                 error("No such file", NULL_PARG);
1632                         break;
1633
1634                 case A_REMOVE_FILE:
1635                         if (ch_getflags() & CH_HELPFILE)
1636                                 break;
1637                         old_ifile = curr_ifile;
1638                         new_ifile = getoff_ifile(curr_ifile);
1639                         if (new_ifile == NULL_IFILE)
1640                         {
1641                                 bell();
1642                                 break;
1643                         }
1644                         if (edit_ifile(new_ifile) != 0)
1645                         {
1646                                 reedit_ifile(old_ifile);
1647                                 break;
1648                         }
1649                         del_ifile(old_ifile);
1650                         break;
1651
1652                 case A_OPT_TOGGLE:
1653                         optflag = OPT_TOGGLE;
1654                         optgetname = FALSE;
1655                         mca_opt_toggle();
1656                         c = getcc();
1657                         goto again;
1658
1659                 case A_DISP_OPTION:
1660                         /*
1661                          * Report a flag setting.
1662                          */
1663                         optflag = OPT_NO_TOGGLE;
1664                         optgetname = FALSE;
1665                         mca_opt_toggle();
1666                         c = getcc();
1667                         goto again;
1668
1669                 case A_FIRSTCMD:
1670                         /*
1671                          * Set an initial command for new files.
1672                          */
1673                         start_mca(A_FIRSTCMD, "+", (void*)NULL, 0);
1674                         c = getcc();
1675                         goto again;
1676
1677                 case A_SHELL:
1678                         /*
1679                          * Shell escape.
1680                          */
1681 #if SHELL_ESCAPE
1682                         if (secure)
1683                         {
1684                                 error("Command not available", NULL_PARG);
1685                                 break;
1686                         }
1687                         start_mca(A_SHELL, "!", ml_shell, 0);
1688                         c = getcc();
1689                         goto again;
1690 #else
1691                         error("Command not available", NULL_PARG);
1692                         break;
1693 #endif
1694
1695                 case A_SETMARK:
1696                         /*
1697                          * Set a mark.
1698                          */
1699                         if (ch_getflags() & CH_HELPFILE)
1700                                 break;
1701                         start_mca(A_SETMARK, "mark: ", (void*)NULL, 0);
1702                         c = getcc();
1703                         if (c == erase_char || c == erase2_char ||
1704                             c == kill_char || c == '\n' || c == '\r')
1705                                 break;
1706                         setmark(c);
1707                         break;
1708
1709                 case A_GOMARK:
1710                         /*
1711                          * Go to a mark.
1712                          */
1713                         start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0);
1714                         c = getcc();
1715                         if (c == erase_char || c == erase2_char ||
1716                             c == kill_char || c == '\n' || c == '\r')
1717                                 break;
1718                         cmd_exec();
1719                         gomark(c);
1720                         break;
1721
1722                 case A_PIPE:
1723 #if PIPEC
1724                         if (secure)
1725                         {
1726                                 error("Command not available", NULL_PARG);
1727                                 break;
1728                         }
1729                         start_mca(A_PIPE, "|mark: ", (void*)NULL, 0);
1730                         c = getcc();
1731                         if (c == erase_char || c == erase2_char || c == kill_char)
1732                                 break;
1733                         if (c == '\n' || c == '\r')
1734                                 c = '.';
1735                         if (badmark(c))
1736                                 break;
1737                         pipec = c;
1738                         start_mca(A_PIPE, "!", ml_shell, 0);
1739                         c = getcc();
1740                         goto again;
1741 #else
1742                         error("Command not available", NULL_PARG);
1743                         break;
1744 #endif
1745
1746                 case A_B_BRACKET:
1747                 case A_F_BRACKET:
1748                         start_mca(action, "Brackets: ", (void*)NULL, 0);
1749                         c = getcc();
1750                         goto again;
1751
1752                 case A_LSHIFT:
1753                         if (number > 0)
1754                                 shift_count = number;
1755                         else
1756                                 number = (shift_count > 0) ?
1757                                         shift_count : sc_width / 2;
1758                         if (number > hshift)
1759                                 number = hshift;
1760                         hshift -= number;
1761                         screen_trashed = 1;
1762                         break;
1763
1764                 case A_RSHIFT:
1765                         if (number > 0)
1766                                 shift_count = number;
1767                         else
1768                                 number = (shift_count > 0) ?
1769                                         shift_count : sc_width / 2;
1770                         hshift += number;
1771                         screen_trashed = 1;
1772                         break;
1773
1774                 case A_PREFIX:
1775                         /*
1776                          * The command is incomplete (more chars are needed).
1777                          * Display the current char, so the user knows
1778                          * what's going on, and get another character.
1779                          */
1780                         if (mca != A_PREFIX)
1781                         {
1782                                 cmd_reset();
1783                                 start_mca(A_PREFIX, " ", (void*)NULL,
1784                                         CF_QUIT_ON_ERASE);
1785                                 (void) cmd_char(c);
1786                         }
1787                         c = getcc();
1788                         goto again;
1789
1790                 case A_NOACTION:
1791                         break;
1792
1793                 default:
1794                         bell();
1795                         break;
1796                 }
1797         }
1798 }