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