1 /*
2 * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice in the documentation and/or other materials provided with
12 * the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
20 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
21 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27
28 /*
29 * User-level command processor.
30 */
31
32 #include "less.h"
33 #include "position.h"
34 #include "option.h"
35 #include "cmd.h"
36
37 extern int erase_char, kill_char;
38 extern int sigs;
39 extern int quit_at_eof;
40 extern int hit_eof;
41 extern int sc_width;
42 extern int sc_height;
43 extern int swindow;
44 extern int jump_sline;
45 extern int quitting;
46 extern int wscroll;
47 extern int nohelp;
48 extern int top_scroll;
49 extern int ignore_eoi;
50 extern char *every_first_cmd;
51 extern char *curr_altfilename;
52 extern char version[];
53 extern struct scrpos initial_scrpos;
54 extern IFILE curr_ifile;
55 #if CMD_HISTORY
56 extern void *ml_search;
57 extern void *ml_examine;
58 #if SHELL_ESCAPE || PIPEC
59 extern void *ml_shell;
60 #endif
61 #else
62 /* No CMD_HISTORY */
63 #define ml_search NULL
64 #define ml_examine NULL
65 #define ml_shell NULL
66 #endif
67 #if EDITOR
68 extern char *editor;
69 extern char *editproto;
70 #endif
71 extern int screen_trashed; /* The screen has been overwritten */
72
73 static char ungot[100];
74 static char *ungotp = NULL;
75 #if SHELL_ESCAPE
76 static char *shellcmd = NULL; /* For holding last shell command for "!!" */
77 #endif
78 static int mca; /* The multicharacter command (action) */
79 static int search_type; /* The previous type of search */
80 static int number; /* The number typed by the user */
81 static char optchar;
82 static int optflag;
83 #if PIPEC
84 static char pipec;
85 #endif
86
87 static void multi_search();
88
89 /*
90 * Move the cursor to lower left before executing a command.
91 * This looks nicer if the command takes a long time before
92 * updating the screen.
93 */
94 static void
cmd_exec()95 cmd_exec()
96 {
97 lower_left();
98 flush();
99 }
100
101 /*
102 * Set up the display to start a new multi-character command.
103 */
104 static void
start_mca(action,prompt,mlist)105 start_mca(action, prompt, mlist)
106 int action;
107 char *prompt;
108 void *mlist;
109 {
110 mca = action;
111 clear_bot();
112 cmd_putstr(prompt);
113 #if CMD_HISTORY
114 set_mlist(mlist);
115 #endif
116 }
117
118 public int
in_mca()119 in_mca()
120 {
121 return (mca != 0 && mca != A_PREFIX);
122 }
123
124 /*
125 * Set up the display to start a new search command.
126 */
127 static void
mca_search()128 mca_search()
129 {
130 if (search_type & SRCH_FORW)
131 mca = A_F_SEARCH;
132 else
133 mca = A_B_SEARCH;
134
135 clear_bot();
136
137 if (search_type & SRCH_FIRST_FILE)
138 cmd_putstr("@");
139
140 if (search_type & SRCH_PAST_EOF)
141 cmd_putstr("*");
142
143 if (search_type & SRCH_NOMATCH)
144 cmd_putstr("!");
145
146 if (search_type & SRCH_FORW)
147 cmd_putstr("/");
148 else
149 cmd_putstr("?");
150 #if CMD_HISTORY
151 set_mlist(ml_search);
152 #endif
153 }
154
155 /*
156 * Execute a multicharacter command.
157 */
158 static void
exec_mca()159 exec_mca()
160 {
161 register char *cbuf;
162
163 cmd_exec();
164 cbuf = get_cmdbuf();
165
166 switch (mca)
167 {
168 case A_F_SEARCH:
169 case A_B_SEARCH:
170 multi_search(cbuf, number);
171 break;
172 case A_FIRSTCMD:
173 /*
174 * Skip leading spaces or + signs in the string.
175 */
176 while (*cbuf == '+' || *cbuf == ' ')
177 cbuf++;
178 if (every_first_cmd != NULL)
179 free(every_first_cmd);
180 if (*cbuf == '\0')
181 every_first_cmd = NULL;
182 else
183 every_first_cmd = save(cbuf);
184 break;
185 case A_OPT_TOGGLE:
186 toggle_option(optchar, cbuf, optflag);
187 optchar = '\0';
188 break;
189 case A_F_BRACKET:
190 match_brac(cbuf[0], cbuf[1], 1, number);
191 break;
192 case A_B_BRACKET:
193 match_brac(cbuf[1], cbuf[0], 0, number);
194 break;
195 #if EXAMINE
196 case A_EXAMINE:
197 edit_list(cbuf);
198 break;
199 #endif
200 #if SHELL_ESCAPE
201 case A_SHELL:
202 /*
203 * !! just uses whatever is in shellcmd.
204 * Otherwise, copy cmdbuf to shellcmd,
205 * expanding any special characters ("%" or "#").
206 */
207 if (*cbuf != '!')
208 {
209 if (shellcmd != NULL)
210 free(shellcmd);
211 shellcmd = fexpand(cbuf);
212 }
213
214 if (shellcmd == NULL)
215 lsystem("");
216 else
217 lsystem(shellcmd);
218 error("!done", NULL_PARG);
219 break;
220 #endif
221 #if PIPEC
222 case A_PIPE:
223 (void) pipe_mark(pipec, cbuf);
224 error("|done", NULL_PARG);
225 break;
226 #endif
227 }
228 }
229
230 /*
231 * Add a character to a multi-character command.
232 */
233 static int
mca_char(c)234 mca_char(c)
235 int c;
236 {
237 char *p;
238 int flag;
239 char buf[3];
240
241 switch (mca)
242 {
243 case 0:
244 /*
245 * Not in a multicharacter command.
246 */
247 return (NO_MCA);
248
249 case A_PREFIX:
250 /*
251 * In the prefix of a command.
252 * This not considered a multichar command
253 * (even tho it uses cmdbuf, etc.).
254 * It is handled in the commands() switch.
255 */
256 return (NO_MCA);
257
258 case A_DIGIT:
259 /*
260 * Entering digits of a number.
261 * Terminated by a non-digit.
262 */
263 if ((c < '0' || c > '9') &&
264 editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE) == A_INVALID)
265 {
266 /*
267 * Not part of the number.
268 * Treat as a normal command character.
269 */
270 number = cmd_int();
271 mca = 0;
272 cmd_accept();
273 return (NO_MCA);
274 }
275 break;
276
277 case A_OPT_TOGGLE:
278 /*
279 * Special case for the TOGGLE_OPTION command.
280 * If the option letter which was entered is a
281 * single-char option, execute the command immediately,
282 * so user doesn't have to hit RETURN.
283 * If the first char is + or -, this indicates
284 * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE.
285 */
286 if (c == erase_char || c == kill_char)
287 break;
288 if (optchar != '\0' && optchar != '+' && optchar != '-')
289 /*
290 * We already have the option letter.
291 */
292 break;
293 switch (c)
294 {
295 case '+':
296 optflag = OPT_UNSET;
297 break;
298 case '-':
299 optflag = OPT_SET;
300 break;
301 default:
302 optchar = c;
303 if (optflag != OPT_TOGGLE || single_char_option(c))
304 {
305 toggle_option(c, "", optflag);
306 return (MCA_DONE);
307 }
308 break;
309 }
310 if (optchar == '+' || optchar == '-')
311 {
312 optchar = c;
313 break;
314 }
315 /*
316 * Display a prompt appropriate for the option letter.
317 */
318 if ((p = opt_prompt(c)) == NULL)
319 {
320 buf[0] = '-';
321 buf[1] = c;
322 buf[2] = '\0';
323 p = buf;
324 }
325 start_mca(A_OPT_TOGGLE, p, (void*)NULL);
326 return (MCA_MORE);
327
328 case A_F_SEARCH:
329 case A_B_SEARCH:
330 /*
331 * Special case for search commands.
332 * Certain characters as the first char of
333 * the pattern have special meaning:
334 * ! Toggle the NOMATCH flag
335 * * Toggle the PAST_EOF flag
336 * @ Toggle the FIRST_FILE flag
337 */
338 if (len_cmdbuf() > 0)
339 /*
340 * Only works for the first char of the pattern.
341 */
342 break;
343
344 flag = 0;
345 switch (c)
346 {
347 case '!':
348 flag = SRCH_NOMATCH;
349 break;
350 case '@':
351 flag = SRCH_FIRST_FILE;
352 break;
353 case '*':
354 flag = SRCH_PAST_EOF;
355 break;
356 }
357 if (flag != 0)
358 {
359 search_type ^= flag;
360 mca_search();
361 return (MCA_MORE);
362 }
363 break;
364 }
365
366 /*
367 * Any other multicharacter command
368 * is terminated by a newline.
369 */
370 if (c == '\n' || c == '\r')
371 {
372 /*
373 * Execute the command.
374 */
375 exec_mca();
376 return (MCA_DONE);
377 }
378 /*
379 * Append the char to the command buffer.
380 */
381 if (cmd_char(c) == CC_QUIT)
382 /*
383 * Abort the multi-char command.
384 */
385 return (MCA_DONE);
386
387 if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
388 {
389 /*
390 * Special case for the bracket-matching commands.
391 * Execute the command after getting exactly two
392 * characters from the user.
393 */
394 exec_mca();
395 return (MCA_DONE);
396 }
397
398 /*
399 * Need another character.
400 */
401 return (MCA_MORE);
402 }
403
404 /*
405 * Display the appropriate prompt.
406 */
407 static void
prompt()408 prompt()
409 {
410 register char *p;
411
412 if (ungotp != NULL && ungotp > ungot)
413 {
414 /*
415 * No prompt necessary if commands are from
416 * ungotten chars rather than from the user.
417 */
418 return;
419 }
420
421 /*
422 * If nothing is displayed yet, display starting from initial_scrpos.
423 */
424 if (empty_screen())
425 {
426 if (initial_scrpos.pos == NULL_POSITION)
427 /*
428 * {{ Maybe this should be:
429 * jump_loc(ch_zero(), jump_sline);
430 * but this behavior seems rather unexpected
431 * on the first screen. }}
432 */
433 jump_loc(ch_zero(), 1);
434 else
435 jump_loc(initial_scrpos.pos, initial_scrpos.ln);
436 } else if (screen_trashed)
437 {
438 int save_top_scroll;
439 save_top_scroll = top_scroll;
440 top_scroll = 1;
441 repaint();
442 top_scroll = save_top_scroll;
443 }
444
445 /*
446 * If the -E flag is set and we've hit EOF on the last file, quit.
447 */
448 if (quit_at_eof == OPT_ONPLUS && hit_eof &&
449 next_ifile(curr_ifile) == NULL_IFILE)
450 quit(QUIT_OK);
451
452 /*
453 * Select the proper prompt and display it.
454 */
455 clear_bot();
456 p = pr_string();
457 if (p == NULL)
458 putchr(':');
459 else
460 {
461 so_enter();
462 putstr(p);
463 so_exit();
464 }
465 }
466
467 public void
dispversion()468 dispversion()
469 {
470 PARG parg;
471
472 parg.p_string = version;
473 #ifndef HANZI
474 error("less version %s", &parg);
475 #else
476 error("cless version %s", &parg);
477 #endif
478 }
479
480 /*
481 * Get command character.
482 * The character normally comes from the keyboard,
483 * but may come from ungotten characters
484 * (characters previously given to ungetcc or ungetsc).
485 */
486 public int
getcc()487 getcc()
488 {
489 if (ungotp == NULL)
490 /*
491 * Normal case: no ungotten chars, so get one from the user.
492 */
493 return (getchr());
494
495 if (ungotp > ungot)
496 /*
497 * Return the next ungotten char.
498 */
499 return (*--ungotp);
500
501 /*
502 * We have just run out of ungotten chars.
503 */
504 ungotp = NULL;
505 if (len_cmdbuf() == 0 || !empty_screen())
506 return (getchr());
507 /*
508 * Command is incomplete, so try to complete it.
509 */
510 switch (mca)
511 {
512 case A_DIGIT:
513 /*
514 * We have a number but no command. Treat as #g.
515 */
516 return ('g');
517
518 case A_F_SEARCH:
519 case A_B_SEARCH:
520 /*
521 * We have "/string" but no newline. Add the \n.
522 */
523 return ('\n');
524
525 default:
526 /*
527 * Some other incomplete command. Let user complete it.
528 */
529 return (getchr());
530 }
531 }
532
533 /*
534 * "Unget" a command character.
535 * The next getcc() will return this character.
536 */
537 public void
ungetcc(c)538 ungetcc(c)
539 int c;
540 {
541 if (ungotp == NULL)
542 ungotp = ungot;
543 if (ungotp >= ungot + sizeof(ungot))
544 {
545 error("ungetcc overflow", NULL_PARG);
546 quit(QUIT_ERROR);
547 }
548 *ungotp++ = c;
549 }
550
551 /*
552 * Unget a whole string of command characters.
553 * The next sequence of getcc()'s will return this string.
554 */
555 public void
ungetsc(s)556 ungetsc(s)
557 char *s;
558 {
559 register char *p;
560
561 for (p = s + strlen(s) - 1; p >= s; p--)
562 ungetcc(*p);
563 }
564
565 /*
566 * Search for a pattern, possibly in multiple files.
567 * If SRCH_FIRST_FILE is set, begin searching at the first file.
568 * If SRCH_PAST_EOF is set, continue the search thru multiple files.
569 */
570 static void
multi_search(pattern,n)571 multi_search(pattern, n)
572 char *pattern;
573 int n;
574 {
575 register int nomore;
576 IFILE save_ifile;
577 int changed_file;
578
579 changed_file = 0;
580 save_ifile = curr_ifile;
581
582 if (search_type & SRCH_FIRST_FILE)
583 {
584 /*
585 * Start at the first (or last) file
586 * in the command line list.
587 */
588 if (search_type & SRCH_FORW)
589 nomore = edit_first();
590 else
591 nomore = edit_last();
592 if (nomore)
593 return;
594 changed_file = 1;
595 search_type &= ~SRCH_FIRST_FILE;
596 }
597
598 for (;;)
599 {
600 if ((n = search(search_type, pattern, n)) == 0)
601 /*
602 * Found it.
603 */
604 return;
605
606 if (n < 0)
607 /*
608 * Some kind of error in the search.
609 * Error message has been printed by search().
610 */
611 break;
612
613 if ((search_type & SRCH_PAST_EOF) == 0)
614 /*
615 * We didn't find a match, but we're
616 * supposed to search only one file.
617 */
618 break;
619 /*
620 * Move on to the next file.
621 */
622 if (search_type & SRCH_FORW)
623 nomore = edit_next(1);
624 else
625 nomore = edit_prev(1);
626 if (nomore)
627 break;
628 changed_file = 1;
629 }
630
631 /*
632 * Didn't find it.
633 * Print an error message if we haven't already.
634 */
635 if (n > 0)
636 error("Pattern not found", NULL_PARG);
637
638 if (changed_file)
639 {
640 /*
641 * Restore the file we were originally viewing.
642 */
643 if (edit_ifile(save_ifile))
644 quit(QUIT_ERROR);
645 }
646 }
647
648 /*
649 * Main command processor.
650 * Accept and execute commands until a quit command.
651 */
652 public void
commands()653 commands()
654 {
655 register int c;
656 register int action;
657 register char *cbuf;
658 int save_search_type;
659 char *s;
660 char tbuf[2];
661 PARG parg;
662
663 search_type = SRCH_FORW;
664 wscroll = (sc_height + 1) / 2;
665
666 for (;;)
667 {
668 mca = 0;
669 cmd_accept();
670 number = 0;
671 optchar = '\0';
672
673 /*
674 * See if any signals need processing.
675 */
676 if (sigs)
677 {
678 psignals();
679 if (quitting)
680 quit(QUIT_SAVED_STATUS);
681 }
682
683 /*
684 * Display prompt and accept a character.
685 */
686 cmd_reset();
687 prompt();
688 if (sigs)
689 continue;
690 c = getcc();
691
692 again:
693 if (sigs)
694 continue;
695
696 /*
697 * If we are in a multicharacter command, call mca_char.
698 * Otherwise we call fcmd_decode to determine the
699 * action to be performed.
700 */
701 if (mca)
702 switch (mca_char(c))
703 {
704 case MCA_MORE:
705 /*
706 * Need another character.
707 */
708 c = getcc();
709 goto again;
710 case MCA_DONE:
711 /*
712 * Command has been handled by mca_char.
713 * Start clean with a prompt.
714 */
715 continue;
716 case NO_MCA:
717 /*
718 * Not a multi-char command
719 * (at least, not anymore).
720 */
721 break;
722 }
723
724 /*
725 * Decode the command character and decide what to do.
726 */
727 if (mca)
728 {
729 /*
730 * We're in a multichar command.
731 * Add the character to the command buffer
732 * and display it on the screen.
733 * If the user backspaces past the start
734 * of the line, abort the command.
735 */
736 if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
737 continue;
738 cbuf = get_cmdbuf();
739 } else
740 {
741 /*
742 * Don't use cmd_char if we're starting fresh
743 * at the beginning of a command, because we
744 * don't want to echo the command until we know
745 * it is a multichar command. We also don't
746 * want erase_char/kill_char to be treated
747 * as line editing characters.
748 */
749 tbuf[0] = c;
750 tbuf[1] = '\0';
751 cbuf = tbuf;
752 }
753 s = NULL;
754 action = fcmd_decode(cbuf, &s);
755 /*
756 * If an "extra" string was returned,
757 * process it as a string of command characters.
758 */
759 if (s != NULL)
760 ungetsc(s);
761 /*
762 * Clear the cmdbuf string.
763 * (But not if we're in the prefix of a command,
764 * because the partial command string is kept there.)
765 */
766 if (action != A_PREFIX)
767 cmd_reset();
768
769 switch (action)
770 {
771 case A_DIGIT:
772 /*
773 * First digit of a number.
774 */
775 start_mca(A_DIGIT, ":", (void*)NULL);
776 goto again;
777
778 case A_F_WINDOW:
779 /*
780 * Forward one window (and set the window size).
781 */
782 if (number > 0)
783 swindow = number;
784 /* FALLTHRU */
785 case A_F_SCREEN:
786 /*
787 * Forward one screen.
788 */
789 if (number <= 0)
790 number = get_swindow();
791 cmd_exec();
792 forward(number, 0, 1);
793 break;
794
795 case A_B_WINDOW:
796 /*
797 * Backward one window (and set the window size).
798 */
799 if (number > 0)
800 swindow = number;
801 /* FALLTHRU */
802 case A_B_SCREEN:
803 /*
804 * Backward one screen.
805 */
806 if (number <= 0)
807 number = get_swindow();
808 cmd_exec();
809 backward(number, 0, 1);
810 break;
811
812 case A_F_LINE:
813 /*
814 * Forward N (default 1) line.
815 */
816 if (number <= 0)
817 number = 1;
818 cmd_exec();
819 forward(number, 0, 0);
820 break;
821
822 case A_B_LINE:
823 /*
824 * Backward N (default 1) line.
825 */
826 if (number <= 0)
827 number = 1;
828 cmd_exec();
829 backward(number, 0, 0);
830 break;
831
832 case A_FF_LINE:
833 /*
834 * Force forward N (default 1) line.
835 */
836 if (number <= 0)
837 number = 1;
838 cmd_exec();
839 forward(number, 1, 0);
840 break;
841
842 case A_BF_LINE:
843 /*
844 * Force backward N (default 1) line.
845 */
846 if (number <= 0)
847 number = 1;
848 cmd_exec();
849 backward(number, 1, 0);
850 break;
851
852 case A_F_FOREVER:
853 /*
854 * Forward forever, ignoring EOF.
855 */
856 cmd_exec();
857 jump_forw();
858 ignore_eoi = 1;
859 hit_eof = 0;
860 while (!ABORT_SIGS())
861 forward(1, 0, 0);
862 ignore_eoi = 0;
863 break;
864
865 case A_F_SCROLL:
866 /*
867 * Forward N lines
868 * (default same as last 'd' or 'u' command).
869 */
870 if (number > 0)
871 wscroll = number;
872 cmd_exec();
873 forward(wscroll, 0, 0);
874 break;
875
876 case A_B_SCROLL:
877 /*
878 * Forward N lines
879 * (default same as last 'd' or 'u' command).
880 */
881 if (number > 0)
882 wscroll = number;
883 cmd_exec();
884 backward(wscroll, 0, 0);
885 break;
886
887 case A_FREPAINT:
888 /*
889 * Flush buffers, then repaint screen.
890 * Don't flush the buffers on a pipe!
891 */
892 if (ch_getflags() & CH_CANSEEK)
893 {
894 ch_flush();
895 clr_linenum();
896 }
897 /* FALLTHRU */
898 case A_REPAINT:
899 /*
900 * Repaint screen.
901 */
902 cmd_exec();
903 repaint();
904 break;
905
906 case A_GOLINE:
907 /*
908 * Go to line N, default beginning of file.
909 */
910 if (number <= 0)
911 number = 1;
912 cmd_exec();
913 jump_back(number);
914 break;
915
916 case A_PERCENT:
917 /*
918 * Go to a specified percentage into the file.
919 */
920 if (number < 0)
921 number = 0;
922 if (number > 100)
923 number = 100;
924 cmd_exec();
925 jump_percent(number);
926 break;
927
928 case A_GOEND:
929 /*
930 * Go to line N, default end of file.
931 */
932 cmd_exec();
933 if (number <= 0)
934 jump_forw();
935 else
936 jump_back(number);
937 break;
938
939 case A_GOPOS:
940 /*
941 * Go to a specified byte position in the file.
942 */
943 cmd_exec();
944 if (number < 0)
945 number = 0;
946 jump_line_loc((POSITION)number, jump_sline);
947 break;
948
949 case A_STAT:
950 /*
951 * Print file name, etc.
952 */
953 cmd_exec();
954 parg.p_string = eq_message();
955 error("%s", &parg);
956 break;
957
958 case A_VERSION:
959 /*
960 * Print version number, without the "@(#)".
961 */
962 cmd_exec();
963 dispversion();
964 break;
965
966 case A_QUIT:
967 /*
968 * Exit.
969 */
970 quit(QUIT_OK);
971
972 /*
973 * Define abbreviation for a commonly used sequence below.
974 */
975 #define DO_SEARCH() if (number <= 0) number = 1; \
976 mca_search(); \
977 cmd_exec(); \
978 multi_search((char *)NULL, number);
979
980
981 case A_F_SEARCH:
982 /*
983 * Search forward for a pattern.
984 * Get the first char of the pattern.
985 */
986 search_type = SRCH_FORW;
987 if (number <= 0)
988 number = 1;
989 mca_search();
990 c = getcc();
991 goto again;
992
993 case A_B_SEARCH:
994 /*
995 * Search backward for a pattern.
996 * Get the first char of the pattern.
997 */
998 search_type = SRCH_BACK;
999 if (number <= 0)
1000 number = 1;
1001 mca_search();
1002 c = getcc();
1003 goto again;
1004
1005 case A_AGAIN_SEARCH:
1006 /*
1007 * Repeat previous search.
1008 */
1009 DO_SEARCH();
1010 break;
1011
1012 case A_T_AGAIN_SEARCH:
1013 /*
1014 * Repeat previous search, multiple files.
1015 */
1016 search_type |= SRCH_PAST_EOF;
1017 DO_SEARCH();
1018 break;
1019
1020 case A_REVERSE_SEARCH:
1021 /*
1022 * Repeat previous search, in reverse direction.
1023 */
1024 save_search_type = search_type;
1025 search_type = SRCH_REVERSE(search_type);
1026 DO_SEARCH();
1027 search_type = save_search_type;
1028 break;
1029
1030 case A_T_REVERSE_SEARCH:
1031 /*
1032 * Repeat previous search,
1033 * multiple files in reverse direction.
1034 */
1035 save_search_type = search_type;
1036 search_type = SRCH_REVERSE(search_type);
1037 search_type |= SRCH_PAST_EOF;
1038 DO_SEARCH();
1039 search_type = save_search_type;
1040 break;
1041
1042 case A_UNDO_SEARCH:
1043 undo_search();
1044 break;
1045
1046 case A_HELP:
1047 /*
1048 * Help.
1049 */
1050 if (nohelp)
1051 {
1052 bell();
1053 break;
1054 }
1055 clear_bot();
1056 putstr(" help");
1057 cmd_exec();
1058 help(0);
1059 break;
1060
1061 case A_EXAMINE:
1062 #if EXAMINE
1063 /*
1064 * Edit a new file. Get the filename.
1065 */
1066 start_mca(A_EXAMINE, "Examine: ", ml_examine);
1067 c = getcc();
1068 goto again;
1069 #else
1070 error("Command not available", NULL_PARG);
1071 break;
1072 #endif
1073
1074 case A_VISUAL:
1075 /*
1076 * Invoke an editor on the input file.
1077 */
1078 #if EDITOR
1079 if (strcmp(get_filename(curr_ifile), "-") == 0)
1080 {
1081 error("Cannot edit standard input", NULL_PARG);
1082 break;
1083 }
1084 if (curr_altfilename != NULL)
1085 {
1086 error("Cannot edit file processed with LESSOPEN",
1087 NULL_PARG);
1088 break;
1089 }
1090 /*
1091 * Expand the editor prototype string
1092 * and pass it to the system to execute.
1093 */
1094 cmd_exec();
1095 lsystem(pr_expand(editproto, 0));
1096 /*
1097 * Re-edit the file, since data may have changed.
1098 * Some editors even recreate the file, so flushing
1099 * buffers is not sufficient.
1100 */
1101 if (edit_ifile(curr_ifile))
1102 quit(QUIT_ERROR);
1103 break;
1104 #else
1105 error("Command not available", NULL_PARG);
1106 break;
1107 #endif
1108
1109 case A_NEXT_FILE:
1110 /*
1111 * Examine next file.
1112 */
1113 if (number <= 0)
1114 number = 1;
1115 if (edit_next(number))
1116 {
1117 if (quit_at_eof && hit_eof)
1118 quit(QUIT_OK);
1119 parg.p_string = (number > 1) ? "(N-th) " : "";
1120 error("No %snext file", &parg);
1121 }
1122 break;
1123
1124 case A_PREV_FILE:
1125 /*
1126 * Examine previous file.
1127 */
1128 if (number <= 0)
1129 number = 1;
1130 if (edit_prev(number))
1131 {
1132 parg.p_string = (number > 1) ? "(N-th) " : "";
1133 error("No %sprevious file", &parg);
1134 }
1135 break;
1136
1137 case A_INDEX_FILE:
1138 /*
1139 * Examine a particular file.
1140 */
1141 if (number <= 0)
1142 number = 1;
1143 if (edit_index(number))
1144 error("No such file", NULL_PARG);
1145 break;
1146
1147 case A_OPT_TOGGLE:
1148 start_mca(A_OPT_TOGGLE, "-", (void*)NULL);
1149 optflag = OPT_TOGGLE;
1150 c = getcc();
1151 goto again;
1152
1153 case A_DISP_OPTION:
1154 /*
1155 * Report a flag setting.
1156 */
1157 start_mca(A_DISP_OPTION, "_", (void*)NULL);
1158 c = getcc();
1159 if (c == erase_char || c == kill_char)
1160 break;
1161 toggle_option(c, "", OPT_NO_TOGGLE);
1162 break;
1163
1164 case A_FIRSTCMD:
1165 /*
1166 * Set an initial command for new files.
1167 */
1168 start_mca(A_FIRSTCMD, "+", (void*)NULL);
1169 c = getcc();
1170 goto again;
1171
1172 case A_SHELL:
1173 /*
1174 * Shell escape.
1175 */
1176 #if SHELL_ESCAPE
1177 start_mca(A_SHELL, "!", ml_shell);
1178 c = getcc();
1179 goto again;
1180 #else
1181 error("Command not available", NULL_PARG);
1182 break;
1183 #endif
1184
1185 case A_SETMARK:
1186 /*
1187 * Set a mark.
1188 */
1189 start_mca(A_SETMARK, "mark: ", (void*)NULL);
1190 c = getcc();
1191 if (c == erase_char || c == kill_char ||
1192 c == '\n' || c == '\r')
1193 break;
1194 setmark(c);
1195 break;
1196
1197 case A_GOMARK:
1198 /*
1199 * Go to a mark.
1200 */
1201 start_mca(A_GOMARK, "goto mark: ", (void*)NULL);
1202 c = getcc();
1203 if (c == erase_char || c == kill_char ||
1204 c == '\n' || c == '\r')
1205 break;
1206 gomark(c);
1207 break;
1208
1209 case A_PIPE:
1210 #if PIPEC
1211 start_mca(A_PIPE, "|mark: ", (void*)NULL);
1212 c = getcc();
1213 if (c == erase_char || c == kill_char)
1214 break;
1215 if (c == '\n' || c == '\r')
1216 c = '.';
1217 if (badmark(c))
1218 break;
1219 pipec = c;
1220 start_mca(A_PIPE, "!", ml_shell);
1221 c = getcc();
1222 goto again;
1223 #else
1224 error("Command not available", NULL_PARG);
1225 break;
1226 #endif
1227
1228 case A_B_BRACKET:
1229 case A_F_BRACKET:
1230 start_mca(action, "Brackets: ", (void*)NULL);
1231 c = getcc();
1232 goto again;
1233
1234 case A_PREFIX:
1235 /*
1236 * The command is incomplete (more chars are needed).
1237 * Display the current char, so the user knows
1238 * what's going on, and get another character.
1239 */
1240 if (mca != A_PREFIX)
1241 {
1242 start_mca(A_PREFIX, " ", (void*)NULL);
1243 cmd_reset();
1244 (void) cmd_char(c);
1245 }
1246 c = getcc();
1247 goto again;
1248
1249 case A_NOACTION:
1250 break;
1251
1252 default:
1253 bell();
1254 break;
1255 }
1256 }
1257 }
1258