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