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