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