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