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