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