da0f3ead67477c5488fca770e54b734a807f781e
[dragonfly.git] / contrib / less-403 / 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         line_left();
89         flush();
90 }
91
92 /*
93  * Set up the display to start a new multi-character command.
94  */
95         static void
96 start_mca(action, prompt, mlist, cmdflags)
97         int action;
98         char *prompt;
99         void *mlist;
100         int cmdflags;
101 {
102         mca = action;
103         clear_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  * Make sure the screen is displayed.
556  */
557         static void
558 make_display()
559 {
560         /*
561          * If nothing is displayed yet, display starting from initial_scrpos.
562          */
563         if (empty_screen())
564         {
565                 if (initial_scrpos.pos == NULL_POSITION)
566                         /*
567                          * {{ Maybe this should be:
568                          *    jump_loc(ch_zero(), jump_sline);
569                          *    but this behavior seems rather unexpected 
570                          *    on the first screen. }}
571                          */
572                         jump_loc(ch_zero(), 1);
573                 else
574                         jump_loc(initial_scrpos.pos, initial_scrpos.ln);
575         } else if (screen_trashed)
576         {
577                 int save_top_scroll;
578                 save_top_scroll = top_scroll;
579                 top_scroll = 1;
580                 repaint();
581                 top_scroll = save_top_scroll;
582         }
583 }
584
585 /*
586  * Display the appropriate prompt.
587  */
588         static void
589 prompt()
590 {
591         register char *p;
592
593         if (ungotp != NULL && ungotp > ungot)
594         {
595                 /*
596                  * No prompt necessary if commands are from 
597                  * ungotten chars rather than from the user.
598                  */
599                 return;
600         }
601
602         /*
603          * Make sure the screen is displayed.
604          */
605         make_display();
606         bottompos = position(BOTTOM_PLUS_ONE);
607
608         /*
609          * If we've hit EOF on the last file, and the -E flag is set
610          * (or -F is set and this is the first prompt), then quit.
611          * {{ Relying on "first prompt" to detect a single-screen file
612          * fails if +G is used, for example. }}
613          */
614         if ((get_quit_at_eof() == OPT_ONPLUS || quit_if_one_screen) &&
615             hit_eof && !(ch_getflags() & CH_HELPFILE) && 
616             next_ifile(curr_ifile) == NULL_IFILE)
617                 quit(QUIT_OK);
618         quit_if_one_screen = FALSE;
619 #if 0 /* This doesn't work well because some "te"s clear the screen. */
620         /*
621          * If the -e flag is set and we've hit EOF on the last file,
622          * and the file is squished (shorter than the screen), quit.
623          */
624         if (get_quit_at_eof() && squished &&
625             next_ifile(curr_ifile) == NULL_IFILE)
626                 quit(QUIT_OK);
627 #endif
628
629 #if MSDOS_COMPILER==WIN32C
630         /* 
631          * In Win32, display the file name in the window title.
632          */
633         if (!(ch_getflags() & CH_HELPFILE))
634                 SetConsoleTitle(pr_expand("Less?f - %f.", 0));
635 #endif
636         /*
637          * Select the proper prompt and display it.
638          */
639         /*
640          * If the previous action was a forward movement, 
641          * don't clear the bottom line of the display;
642          * just print the prompt since the forward movement guarantees 
643          * that we're in the right position to display the prompt.
644          * Clearing the line could cause a problem: for example, if the last
645          * line displayed ended at the right screen edge without a newline,
646          * then clearing would clear the last displayed line rather than
647          * the prompt line.
648          */
649         if (!forw_prompt)
650                 clear_bot();
651         clear_cmd();
652         forw_prompt = 0;
653         p = pr_string();
654         if (p == NULL || *p == '\0')
655                 putchr(':');
656         else
657         {
658                 at_enter(AT_STANDOUT);
659                 putstr(p);
660                 at_exit();
661         }
662         clear_eol();
663 }
664
665 /*
666  * Display the less version message.
667  */
668         public void
669 dispversion()
670 {
671         PARG parg;
672
673         parg.p_string = version;
674         error("less %s", &parg);
675 }
676
677 /*
678  * Get command character.
679  * The character normally comes from the keyboard,
680  * but may come from ungotten characters
681  * (characters previously given to ungetcc or ungetsc).
682  */
683         public int
684 getcc()
685 {
686         if (ungotp == NULL)
687                 /*
688                  * Normal case: no ungotten chars, so get one from the user.
689                  */
690                 return (getchr());
691
692         if (ungotp > ungot)
693                 /*
694                  * Return the next ungotten char.
695                  */
696                 return (*--ungotp);
697
698         /*
699          * We have just run out of ungotten chars.
700          */
701         ungotp = NULL;
702         if (len_cmdbuf() == 0 || !empty_screen())
703                 return (getchr());
704         /*
705          * Command is incomplete, so try to complete it.
706          */
707         switch (mca)
708         {
709         case A_DIGIT:
710                 /*
711                  * We have a number but no command.  Treat as #g.
712                  */
713                 return ('g');
714
715         case A_F_SEARCH:
716         case A_B_SEARCH:
717                 /*
718                  * We have "/string" but no newline.  Add the \n.
719                  */
720                 return ('\n'); 
721
722         default:
723                 /*
724                  * Some other incomplete command.  Let user complete it.
725                  */
726                 return (getchr());
727         }
728 }
729
730 /*
731  * "Unget" a command character.
732  * The next getcc() will return this character.
733  */
734         public void
735 ungetcc(c)
736         int c;
737 {
738         if (ungotp == NULL)
739                 ungotp = ungot;
740         if (ungotp >= ungot + sizeof(ungot))
741         {
742                 error("ungetcc overflow", NULL_PARG);
743                 quit(QUIT_ERROR);
744         }
745         *ungotp++ = c;
746 }
747
748 /*
749  * Unget a whole string of command characters.
750  * The next sequence of getcc()'s will return this string.
751  */
752         public void
753 ungetsc(s)
754         char *s;
755 {
756         register char *p;
757
758         for (p = s + strlen(s) - 1;  p >= s;  p--)
759                 ungetcc(*p);
760 }
761
762 /*
763  * Search for a pattern, possibly in multiple files.
764  * If SRCH_FIRST_FILE is set, begin searching at the first file.
765  * If SRCH_PAST_EOF is set, continue the search thru multiple files.
766  */
767         static void
768 multi_search(pattern, n)
769         char *pattern;
770         int n;
771 {
772         register int nomore;
773         IFILE save_ifile;
774         int changed_file;
775
776         changed_file = 0;
777         save_ifile = save_curr_ifile();
778
779         if (search_type & SRCH_FIRST_FILE)
780         {
781                 /*
782                  * Start at the first (or last) file 
783                  * in the command line list.
784                  */
785                 if (search_type & SRCH_FORW)
786                         nomore = edit_first();
787                 else
788                         nomore = edit_last();
789                 if (nomore)
790                 {
791                         unsave_ifile(save_ifile);
792                         return;
793                 }
794                 changed_file = 1;
795                 search_type &= ~SRCH_FIRST_FILE;
796         }
797
798         for (;;)
799         {
800                 n = search(search_type, pattern, n);
801                 /*
802                  * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared
803                  * after being used once.  This allows "n" to work after
804                  * using a /@@ search.
805                  */
806                 search_type &= ~SRCH_NO_MOVE;
807                 if (n == 0)
808                 {
809                         /*
810                          * Found it.
811                          */
812                         unsave_ifile(save_ifile);
813                         return;
814                 }
815
816                 if (n < 0)
817                         /*
818                          * Some kind of error in the search.
819                          * Error message has been printed by search().
820                          */
821                         break;
822
823                 if ((search_type & SRCH_PAST_EOF) == 0)
824                         /*
825                          * We didn't find a match, but we're
826                          * supposed to search only one file.
827                          */
828                         break;
829                 /*
830                  * Move on to the next file.
831                  */
832                 if (search_type & SRCH_FORW)
833                         nomore = edit_next(1);
834                 else
835                         nomore = edit_prev(1);
836                 if (nomore)
837                         break;
838                 changed_file = 1;
839         }
840
841         /*
842          * Didn't find it.
843          * Print an error message if we haven't already.
844          */
845         if (n > 0)
846                 error("Pattern not found", NULL_PARG);
847
848         if (changed_file)
849         {
850                 /*
851                  * Restore the file we were originally viewing.
852                  */
853                 reedit_ifile(save_ifile);
854         } else
855         {
856                 unsave_ifile(save_ifile);
857         }
858 }
859
860 /*
861  * Main command processor.
862  * Accept and execute commands until a quit command.
863  */
864         public void
865 commands()
866 {
867         register int c;
868         register int action;
869         register char *cbuf;
870         int newaction;
871         int save_search_type;
872         char *extra;
873         char tbuf[2];
874         PARG parg;
875         IFILE old_ifile;
876         IFILE new_ifile;
877         char *tagfile;
878
879         search_type = SRCH_FORW;
880         wscroll = (sc_height + 1) / 2;
881         newaction = A_NOACTION;
882
883         for (;;)
884         {
885                 mca = 0;
886                 cmd_accept();
887                 number = 0;
888                 optchar = '\0';
889
890                 /*
891                  * See if any signals need processing.
892                  */
893                 if (sigs)
894                 {
895                         psignals();
896                         if (quitting)
897                                 quit(QUIT_SAVED_STATUS);
898                 }
899
900                 /*
901                  * See if window size changed, for systems that don't
902                  * generate SIGWINCH.
903                  */
904                 check_winch();
905
906                 /*
907                  * Display prompt and accept a character.
908                  */
909                 cmd_reset();
910                 prompt();
911                 if (sigs)
912                         continue;
913                 if (newaction == A_NOACTION)
914                         c = getcc();
915
916         again:
917                 if (sigs)
918                         continue;
919
920                 if (newaction != A_NOACTION)
921                 {
922                         action = newaction;
923                         newaction = A_NOACTION;
924                 } else
925                 {
926                         /*
927                          * If we are in a multicharacter command, call mca_char.
928                          * Otherwise we call fcmd_decode to determine the
929                          * action to be performed.
930                          */
931                         if (mca)
932                                 switch (mca_char(c))
933                                 {
934                                 case MCA_MORE:
935                                         /*
936                                          * Need another character.
937                                          */
938                                         c = getcc();
939                                         goto again;
940                                 case MCA_DONE:
941                                         /*
942                                          * Command has been handled by mca_char.
943                                          * Start clean with a prompt.
944                                          */
945                                         continue;
946                                 case NO_MCA:
947                                         /*
948                                          * Not a multi-char command
949                                          * (at least, not anymore).
950                                          */
951                                         break;
952                                 }
953
954                         /*
955                          * Decode the command character and decide what to do.
956                          */
957                         if (mca)
958                         {
959                                 /*
960                                  * We're in a multichar command.
961                                  * Add the character to the command buffer
962                                  * and display it on the screen.
963                                  * If the user backspaces past the start 
964                                  * of the line, abort the command.
965                                  */
966                                 if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
967                                         continue;
968                                 cbuf = get_cmdbuf();
969                         } else
970                         {
971                                 /*
972                                  * Don't use cmd_char if we're starting fresh
973                                  * at the beginning of a command, because we
974                                  * don't want to echo the command until we know
975                                  * it is a multichar command.  We also don't
976                                  * want erase_char/kill_char to be treated
977                                  * as line editing characters.
978                                  */
979                                 tbuf[0] = c;
980                                 tbuf[1] = '\0';
981                                 cbuf = tbuf;
982                         }
983                         extra = NULL;
984                         action = fcmd_decode(cbuf, &extra);
985                         /*
986                          * If an "extra" string was returned,
987                          * process it as a string of command characters.
988                          */
989                         if (extra != NULL)
990                                 ungetsc(extra);
991                 }
992                 /*
993                  * Clear the cmdbuf string.
994                  * (But not if we're in the prefix of a command,
995                  * because the partial command string is kept there.)
996                  */
997                 if (action != A_PREFIX)
998                         cmd_reset();
999
1000                 switch (action)
1001                 {
1002                 case A_DIGIT:
1003                         /*
1004                          * First digit of a number.
1005                          */
1006                         start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE);
1007                         goto again;
1008
1009                 case A_F_WINDOW:
1010                         /*
1011                          * Forward one window (and set the window size).
1012                          */
1013                         if (number > 0)
1014                                 swindow = (int) number;
1015                         /* FALLTHRU */
1016                 case A_F_SCREEN:
1017                         /*
1018                          * Forward one screen.
1019                          */
1020                         if (number <= 0)
1021                                 number = get_swindow();
1022                         cmd_exec();
1023                         if (show_attn)
1024                                 set_attnpos(bottompos);
1025                         forward((int) number, 0, 1);
1026                         break;
1027
1028                 case A_B_WINDOW:
1029                         /*
1030                          * Backward one window (and set the window size).
1031                          */
1032                         if (number > 0)
1033                                 swindow = (int) number;
1034                         /* FALLTHRU */
1035                 case A_B_SCREEN:
1036                         /*
1037                          * Backward one screen.
1038                          */
1039                         if (number <= 0)
1040                                 number = get_swindow();
1041                         cmd_exec();
1042                         backward((int) number, 0, 1);
1043                         break;
1044
1045                 case A_F_LINE:
1046                         /*
1047                          * Forward N (default 1) line.
1048                          */
1049                         if (number <= 0)
1050                                 number = 1;
1051                         cmd_exec();
1052                         if (show_attn == OPT_ONPLUS && number > 1)
1053                                 set_attnpos(bottompos);
1054                         forward((int) number, 0, 0);
1055                         break;
1056
1057                 case A_B_LINE:
1058                         /*
1059                          * Backward N (default 1) line.
1060                          */
1061                         if (number <= 0)
1062                                 number = 1;
1063                         cmd_exec();
1064                         backward((int) number, 0, 0);
1065                         break;
1066
1067                 case A_FF_LINE:
1068                         /*
1069                          * Force forward N (default 1) line.
1070                          */
1071                         if (number <= 0)
1072                                 number = 1;
1073                         cmd_exec();
1074                         if (show_attn == OPT_ONPLUS && number > 1)
1075                                 set_attnpos(bottompos);
1076                         forward((int) number, 1, 0);
1077                         break;
1078
1079                 case A_BF_LINE:
1080                         /*
1081                          * Force backward N (default 1) line.
1082                          */
1083                         if (number <= 0)
1084                                 number = 1;
1085                         cmd_exec();
1086                         backward((int) number, 1, 0);
1087                         break;
1088                 
1089                 case A_FF_SCREEN:
1090                         /*
1091                          * Force forward one screen.
1092                          */
1093                         if (number <= 0)
1094                                 number = get_swindow();
1095                         cmd_exec();
1096                         if (show_attn == OPT_ONPLUS)
1097                                 set_attnpos(bottompos);
1098                         forward((int) number, 1, 0);
1099                         break;
1100
1101                 case A_F_FOREVER:
1102                         /*
1103                          * Forward forever, ignoring EOF.
1104                          */
1105                         if (ch_getflags() & CH_HELPFILE)
1106                                 break;
1107                         cmd_exec();
1108                         jump_forw();
1109                         ignore_eoi = 1;
1110                         hit_eof = 0;
1111                         while (!sigs)
1112                                 forward(1, 0, 0);
1113                         ignore_eoi = 0;
1114                         /*
1115                          * This gets us back in "F mode" after processing 
1116                          * a non-abort signal (e.g. window-change).  
1117                          */
1118                         if (sigs && !ABORT_SIGS())
1119                                 newaction = A_F_FOREVER;
1120                         break;
1121
1122                 case A_F_SCROLL:
1123                         /*
1124                          * Forward N lines 
1125                          * (default same as last 'd' or 'u' command).
1126                          */
1127                         if (number > 0)
1128                                 wscroll = (int) number;
1129                         cmd_exec();
1130                         if (show_attn == OPT_ONPLUS)
1131                                 set_attnpos(bottompos);
1132                         forward(wscroll, 0, 0);
1133                         break;
1134
1135                 case A_B_SCROLL:
1136                         /*
1137                          * Forward N lines 
1138                          * (default same as last 'd' or 'u' command).
1139                          */
1140                         if (number > 0)
1141                                 wscroll = (int) number;
1142                         cmd_exec();
1143                         backward(wscroll, 0, 0);
1144                         break;
1145
1146                 case A_FREPAINT:
1147                         /*
1148                          * Flush buffers, then repaint screen.
1149                          * Don't flush the buffers on a pipe!
1150                          */
1151                         if (ch_getflags() & CH_CANSEEK)
1152                         {
1153                                 ch_flush();
1154                                 clr_linenum();
1155 #if HILITE_SEARCH
1156                                 clr_hilite();
1157 #endif
1158                         }
1159                         /* FALLTHRU */
1160                 case A_REPAINT:
1161                         /*
1162                          * Repaint screen.
1163                          */
1164                         cmd_exec();
1165                         repaint();
1166                         break;
1167
1168                 case A_GOLINE:
1169                         /*
1170                          * Go to line N, default beginning of file.
1171                          */
1172                         if (number <= 0)
1173                                 number = 1;
1174                         cmd_exec();
1175                         jump_back(number);
1176                         break;
1177
1178                 case A_PERCENT:
1179                         /*
1180                          * Go to a specified percentage into the file.
1181                          */
1182                         if (number < 0)
1183                         {
1184                                 number = 0;
1185                                 fraction = 0;
1186                         }
1187                         if (number > 100)
1188                         {
1189                                 number = 100;
1190                                 fraction = 0;
1191                         }
1192                         cmd_exec();
1193                         jump_percent((int) number, fraction);
1194                         break;
1195
1196                 case A_GOEND:
1197                         /*
1198                          * Go to line N, default end of file.
1199                          */
1200                         cmd_exec();
1201                         if (number <= 0)
1202                                 jump_forw();
1203                         else
1204                                 jump_back(number);
1205                         break;
1206
1207                 case A_GOPOS:
1208                         /*
1209                          * Go to a specified byte position in the file.
1210                          */
1211                         cmd_exec();
1212                         if (number < 0)
1213                                 number = 0;
1214                         jump_line_loc((POSITION) number, jump_sline);
1215                         break;
1216
1217                 case A_STAT:
1218                         /*
1219                          * Print file name, etc.
1220                          */
1221                         if (ch_getflags() & CH_HELPFILE)
1222                                 break;
1223                         cmd_exec();
1224                         parg.p_string = eq_message();
1225                         error("%s", &parg);
1226                         break;
1227
1228                 case A_VERSION:
1229                         /*
1230                          * Print version number, without the "@(#)".
1231                          */
1232                         cmd_exec();
1233                         dispversion();
1234                         break;
1235
1236                 case A_QUIT:
1237                         /*
1238                          * Exit.
1239                          */
1240                         if (curr_ifile != NULL_IFILE && 
1241                             ch_getflags() & CH_HELPFILE)
1242                         {
1243                                 /*
1244                                  * Quit while viewing the help file
1245                                  * just means return to viewing the
1246                                  * previous file.
1247                                  */
1248                                 hshift = save_hshift;
1249                                 if (edit_prev(1) == 0)
1250                                         break;
1251                         }
1252                         if (extra != NULL)
1253                                 quit(*extra);
1254                         quit(QUIT_OK);
1255                         break;
1256
1257 /*
1258  * Define abbreviation for a commonly used sequence below.
1259  */
1260 #define DO_SEARCH()     if (number <= 0) number = 1;    \
1261                         mca_search();                   \
1262                         cmd_exec();                     \
1263                         multi_search((char *)NULL, (int) number);
1264
1265
1266                 case A_F_SEARCH:
1267                         /*
1268                          * Search forward for a pattern.
1269                          * Get the first char of the pattern.
1270                          */
1271                         search_type = SRCH_FORW;
1272                         if (number <= 0)
1273                                 number = 1;
1274                         mca_search();
1275                         c = getcc();
1276                         goto again;
1277
1278                 case A_B_SEARCH:
1279                         /*
1280                          * Search backward for a pattern.
1281                          * Get the first char of the pattern.
1282                          */
1283                         search_type = SRCH_BACK;
1284                         if (number <= 0)
1285                                 number = 1;
1286                         mca_search();
1287                         c = getcc();
1288                         goto again;
1289
1290                 case A_AGAIN_SEARCH:
1291                         /*
1292                          * Repeat previous search.
1293                          */
1294                         DO_SEARCH();
1295                         break;
1296                 
1297                 case A_T_AGAIN_SEARCH:
1298                         /*
1299                          * Repeat previous search, multiple files.
1300                          */
1301                         search_type |= SRCH_PAST_EOF;
1302                         DO_SEARCH();
1303                         break;
1304
1305                 case A_REVERSE_SEARCH:
1306                         /*
1307                          * Repeat previous search, in reverse direction.
1308                          */
1309                         save_search_type = search_type;
1310                         search_type = SRCH_REVERSE(search_type);
1311                         DO_SEARCH();
1312                         search_type = save_search_type;
1313                         break;
1314
1315                 case A_T_REVERSE_SEARCH:
1316                         /* 
1317                          * Repeat previous search, 
1318                          * multiple files in reverse direction.
1319                          */
1320                         save_search_type = search_type;
1321                         search_type = SRCH_REVERSE(search_type);
1322                         search_type |= SRCH_PAST_EOF;
1323                         DO_SEARCH();
1324                         search_type = save_search_type;
1325                         break;
1326
1327                 case A_UNDO_SEARCH:
1328                         undo_search();
1329                         break;
1330
1331                 case A_HELP:
1332                         /*
1333                          * Help.
1334                          */
1335                         if (ch_getflags() & CH_HELPFILE)
1336                                 break;
1337                         cmd_exec();
1338                         save_hshift = hshift;
1339                         hshift = 0;
1340                         (void) edit(FAKE_HELPFILE);
1341                         break;
1342
1343                 case A_EXAMINE:
1344 #if EXAMINE
1345                         /*
1346                          * Edit a new file.  Get the filename.
1347                          */
1348                         if (secure)
1349                         {
1350                                 error("Command not available", NULL_PARG);
1351                                 break;
1352                         }
1353                         start_mca(A_EXAMINE, "Examine: ", ml_examine, 0);
1354                         c = getcc();
1355                         goto again;
1356 #else
1357                         error("Command not available", NULL_PARG);
1358                         break;
1359 #endif
1360                         
1361                 case A_VISUAL:
1362                         /*
1363                          * Invoke an editor on the input file.
1364                          */
1365 #if EDITOR
1366                         if (secure)
1367                         {
1368                                 error("Command not available", NULL_PARG);
1369                                 break;
1370                         }
1371                         if (ch_getflags() & CH_HELPFILE)
1372                                 break;
1373                         if (strcmp(get_filename(curr_ifile), "-") == 0)
1374                         {
1375                                 error("Cannot edit standard input", NULL_PARG);
1376                                 break;
1377                         }
1378                         if (curr_altfilename != NULL)
1379                         {
1380                                 error("WARNING: This file was viewed via LESSOPEN",
1381                                         NULL_PARG);
1382                         }
1383                         start_mca(A_SHELL, "!", ml_shell, 0);
1384                         /*
1385                          * Expand the editor prototype string
1386                          * and pass it to the system to execute.
1387                          * (Make sure the screen is displayed so the
1388                          * expansion of "+%lm" works.)
1389                          */
1390                         make_display();
1391                         cmd_exec();
1392                         lsystem(pr_expand(editproto, 0), (char*)NULL);
1393                         break;
1394 #else
1395                         error("Command not available", NULL_PARG);
1396                         break;
1397 #endif
1398
1399                 case A_NEXT_FILE:
1400                         /*
1401                          * Examine next file.
1402                          */
1403 #if TAGS
1404                         if (ntags())
1405                         {
1406                                 error("No next file", NULL_PARG);
1407                                 break;
1408                         }
1409 #endif
1410                         if (number <= 0)
1411                                 number = 1;
1412                         if (edit_next((int) number))
1413                         {
1414                                 if (get_quit_at_eof() && hit_eof && 
1415                                     !(ch_getflags() & CH_HELPFILE))
1416                                         quit(QUIT_OK);
1417                                 parg.p_string = (number > 1) ? "(N-th) " : "";
1418                                 error("No %snext file", &parg);
1419                         }
1420                         break;
1421
1422                 case A_PREV_FILE:
1423                         /*
1424                          * Examine previous file.
1425                          */
1426 #if TAGS
1427                         if (ntags())
1428                         {
1429                                 error("No previous file", NULL_PARG);
1430                                 break;
1431                         }
1432 #endif
1433                         if (number <= 0)
1434                                 number = 1;
1435                         if (edit_prev((int) number))
1436                         {
1437                                 parg.p_string = (number > 1) ? "(N-th) " : "";
1438                                 error("No %sprevious file", &parg);
1439                         }
1440                         break;
1441
1442                 case A_NEXT_TAG:
1443 #if TAGS
1444                         if (number <= 0)
1445                                 number = 1;
1446                         tagfile = nexttag((int) number);
1447                         if (tagfile == NULL)
1448                         {
1449                                 error("No next tag", NULL_PARG);
1450                                 break;
1451                         }
1452                         if (edit(tagfile) == 0)
1453                         {
1454                                 POSITION pos = tagsearch();
1455                                 if (pos != NULL_POSITION)
1456                                         jump_loc(pos, jump_sline);
1457                         }
1458 #else
1459                         error("Command not available", NULL_PARG);
1460 #endif
1461                         break;
1462
1463                 case A_PREV_TAG:
1464 #if TAGS
1465                         if (number <= 0)
1466                                 number = 1;
1467                         tagfile = prevtag((int) number);
1468                         if (tagfile == NULL)
1469                         {
1470                                 error("No previous 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_INDEX_FILE:
1485                         /*
1486                          * Examine a particular file.
1487                          */
1488                         if (number <= 0)
1489                                 number = 1;
1490                         if (edit_index((int) number))
1491                                 error("No such file", NULL_PARG);
1492                         break;
1493
1494                 case A_REMOVE_FILE:
1495                         if (ch_getflags() & CH_HELPFILE)
1496                                 break;
1497                         old_ifile = curr_ifile;
1498                         new_ifile = getoff_ifile(curr_ifile);
1499                         if (new_ifile == NULL_IFILE)
1500                         {
1501                                 bell();
1502                                 break;
1503                         }
1504                         if (edit_ifile(new_ifile) != 0)
1505                         {
1506                                 reedit_ifile(old_ifile);
1507                                 break;
1508                         }
1509                         del_ifile(old_ifile);
1510                         break;
1511
1512                 case A_OPT_TOGGLE:
1513                         optflag = OPT_TOGGLE;
1514                         optgetname = FALSE;
1515                         mca_opt_toggle();
1516                         c = getcc();
1517                         goto again;
1518
1519                 case A_DISP_OPTION:
1520                         /*
1521                          * Report a flag setting.
1522                          */
1523                         optflag = OPT_NO_TOGGLE;
1524                         optgetname = FALSE;
1525                         mca_opt_toggle();
1526                         c = getcc();
1527                         goto again;
1528
1529                 case A_FIRSTCMD:
1530                         /*
1531                          * Set an initial command for new files.
1532                          */
1533                         start_mca(A_FIRSTCMD, "+", (void*)NULL, 0);
1534                         c = getcc();
1535                         goto again;
1536
1537                 case A_SHELL:
1538                         /*
1539                          * Shell escape.
1540                          */
1541 #if SHELL_ESCAPE
1542                         if (secure)
1543                         {
1544                                 error("Command not available", NULL_PARG);
1545                                 break;
1546                         }
1547                         start_mca(A_SHELL, "!", ml_shell, 0);
1548                         c = getcc();
1549                         goto again;
1550 #else
1551                         error("Command not available", NULL_PARG);
1552                         break;
1553 #endif
1554
1555                 case A_SETMARK:
1556                         /*
1557                          * Set a mark.
1558                          */
1559                         if (ch_getflags() & CH_HELPFILE)
1560                                 break;
1561                         start_mca(A_SETMARK, "mark: ", (void*)NULL, 0);
1562                         c = getcc();
1563                         if (c == erase_char || c == erase2_char ||
1564                             c == kill_char || c == '\n' || c == '\r')
1565                                 break;
1566                         setmark(c);
1567                         break;
1568
1569                 case A_GOMARK:
1570                         /*
1571                          * Go to a mark.
1572                          */
1573                         start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0);
1574                         c = getcc();
1575                         if (c == erase_char || c == erase2_char ||
1576                             c == kill_char || c == '\n' || c == '\r')
1577                                 break;
1578                         gomark(c);
1579                         break;
1580
1581                 case A_PIPE:
1582 #if PIPEC
1583                         if (secure)
1584                         {
1585                                 error("Command not available", NULL_PARG);
1586                                 break;
1587                         }
1588                         start_mca(A_PIPE, "|mark: ", (void*)NULL, 0);
1589                         c = getcc();
1590                         if (c == erase_char || c == erase2_char || c == kill_char)
1591                                 break;
1592                         if (c == '\n' || c == '\r')
1593                                 c = '.';
1594                         if (badmark(c))
1595                                 break;
1596                         pipec = c;
1597                         start_mca(A_PIPE, "!", ml_shell, 0);
1598                         c = getcc();
1599                         goto again;
1600 #else
1601                         error("Command not available", NULL_PARG);
1602                         break;
1603 #endif
1604
1605                 case A_B_BRACKET:
1606                 case A_F_BRACKET:
1607                         start_mca(action, "Brackets: ", (void*)NULL, 0);
1608                         c = getcc();
1609                         goto again;
1610
1611                 case A_LSHIFT:
1612                         if (number > 0)
1613                                 shift_count = number;
1614                         else
1615                                 number = (shift_count > 0) ?
1616                                         shift_count : sc_width / 2;
1617                         if (number > hshift)
1618                                 number = hshift;
1619                         hshift -= number;
1620                         screen_trashed = 1;
1621                         break;
1622
1623                 case A_RSHIFT:
1624                         if (number > 0)
1625                                 shift_count = number;
1626                         else
1627                                 number = (shift_count > 0) ?
1628                                         shift_count : sc_width / 2;
1629                         hshift += number;
1630                         screen_trashed = 1;
1631                         break;
1632
1633                 case A_PREFIX:
1634                         /*
1635                          * The command is incomplete (more chars are needed).
1636                          * Display the current char, so the user knows
1637                          * what's going on, and get another character.
1638                          */
1639                         if (mca != A_PREFIX)
1640                         {
1641                                 cmd_reset();
1642                                 start_mca(A_PREFIX, " ", (void*)NULL,
1643                                         CF_QUIT_ON_ERASE);
1644                                 (void) cmd_char(c);
1645                         }
1646                         c = getcc();
1647                         goto again;
1648
1649                 case A_NOACTION:
1650                         break;
1651
1652                 default:
1653                         bell();
1654                         break;
1655                 }
1656         }
1657 }