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