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