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