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