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