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