"SfR Fresh" - the SfR Freeware/Shareware Archive 
Member "less-424/command.c" of archive less-424.tar.gz:
As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers.
Alternatively you can here view or download the uninterpreted source code file.
That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
1 /*
2 * Copyright (C) 1984-2008 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 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 show_attn;
39 extern char *every_first_cmd;
40 extern char *curr_altfilename;
41 extern char version[];
42 extern struct scrpos initial_scrpos;
43 extern IFILE curr_ifile;
44 extern void constant *ml_search;
45 extern void constant *ml_examine;
46 #if SHELL_ESCAPE || PIPEC
47 extern void constant *ml_shell;
48 #endif
49 #if EDITOR
50 extern char *editor;
51 extern char *editproto;
52 #endif
53 extern int screen_trashed; /* The screen has been overwritten */
54 extern int shift_count;
55 extern int oldbot;
56 extern int forw_prompt;
57
58 static char ungot[UNGOT_SIZE];
59 static char *ungotp = NULL;
60 #if SHELL_ESCAPE
61 static char *shellcmd = NULL; /* For holding last shell command for "!!" */
62 #endif
63 static int mca; /* The multicharacter command (action) */
64 static int search_type; /* The previous type of search */
65 static LINENUM number; /* The number typed by the user */
66 static long fraction; /* The fractional part of the number */
67 static char optchar;
68 static int optflag;
69 static int optgetname;
70 static POSITION bottompos;
71 static int save_hshift;
72 #if PIPEC
73 static char pipec;
74 #endif
75
76 static void multi_search();
77
78 /*
79 * Move the cursor to start of prompt line before executing a command.
80 * This looks nicer if the command takes a long time before
81 * updating the screen.
82 */
83 static void
84 cmd_exec()
85 {
86 #if HILITE_SEARCH
87 clear_attn();
88 #endif
89 clear_bot();
90 flush();
91 }
92
93 /*
94 * Set up the display to start a new multi-character command.
95 */
96 static void
97 start_mca(action, prompt, mlist, cmdflags)
98 int action;
99 char *prompt;
100 void *mlist;
101 int cmdflags;
102 {
103 mca = action;
104 clear_bot();
105 clear_cmd();
106 cmd_putstr(prompt);
107 set_mlist(mlist, cmdflags);
108 }
109
110 public int
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
120 mca_search()
121 {
122 #if HILITE_SEARCH
123 if (search_type & SRCH_FILTER)
124 mca = A_FILTER;
125 else
126 #endif
127 if (search_type & SRCH_FORW)
128 mca = A_F_SEARCH;
129 else
130 mca = A_B_SEARCH;
131
132 clear_bot();
133 clear_cmd();
134
135 if (search_type & SRCH_NO_MATCH)
136 cmd_putstr("Non-match ");
137 if (search_type & SRCH_FIRST_FILE)
138 cmd_putstr("First-file ");
139 if (search_type & SRCH_PAST_EOF)
140 cmd_putstr("EOF-ignore ");
141 if (search_type & SRCH_NO_MOVE)
142 cmd_putstr("Keep-pos ");
143 if (search_type & SRCH_NO_REGEX)
144 cmd_putstr("Regex-off ");
145
146 #if HILITE_SEARCH
147 if (search_type & SRCH_FILTER)
148 cmd_putstr("&/");
149 else
150 #endif
151 if (search_type & SRCH_FORW)
152 cmd_putstr("/");
153 else
154 cmd_putstr("?");
155 set_mlist(ml_search, 0);
156 }
157
158 /*
159 * Set up the display to start a new toggle-option command.
160 */
161 static void
162 mca_opt_toggle()
163 {
164 int no_prompt;
165 int flag;
166 char *dash;
167
168 no_prompt = (optflag & OPT_NO_PROMPT);
169 flag = (optflag & ~OPT_NO_PROMPT);
170 dash = (flag == OPT_NO_TOGGLE) ? "_" : "-";
171
172 mca = A_OPT_TOGGLE;
173 clear_bot();
174 clear_cmd();
175 cmd_putstr(dash);
176 if (optgetname)
177 cmd_putstr(dash);
178 if (no_prompt)
179 cmd_putstr("(P)");
180 switch (flag)
181 {
182 case OPT_UNSET:
183 cmd_putstr("+");
184 break;
185 case OPT_SET:
186 cmd_putstr("!");
187 break;
188 }
189 set_mlist(NULL, 0);
190 }
191
192 /*
193 * Execute a multicharacter command.
194 */
195 static void
196 exec_mca()
197 {
198 register char *cbuf;
199
200 cmd_exec();
201 cbuf = get_cmdbuf();
202
203 switch (mca)
204 {
205 case A_F_SEARCH:
206 case A_B_SEARCH:
207 multi_search(cbuf, (int) number);
208 break;
209 #if HILITE_SEARCH
210 case A_FILTER:
211 search_type ^= SRCH_NO_MATCH;
212 set_filter_pattern(cbuf, search_type);
213 break;
214 #endif
215 case A_FIRSTCMD:
216 /*
217 * Skip leading spaces or + signs in the string.
218 */
219 while (*cbuf == '+' || *cbuf == ' ')
220 cbuf++;
221 if (every_first_cmd != NULL)
222 free(every_first_cmd);
223 if (*cbuf == '\0')
224 every_first_cmd = NULL;
225 else
226 every_first_cmd = save(cbuf);
227 break;
228 case A_OPT_TOGGLE:
229 toggle_option(optchar, cbuf, optflag);
230 optchar = '\0';
231 break;
232 case A_F_BRACKET:
233 match_brac(cbuf[0], cbuf[1], 1, (int) number);
234 break;
235 case A_B_BRACKET:
236 match_brac(cbuf[1], cbuf[0], 0, (int) number);
237 break;
238 #if EXAMINE
239 case A_EXAMINE:
240 if (secure)
241 break;
242 edit_list(cbuf);
243 #if TAGS
244 /* If tag structure is loaded then clean it up. */
245 cleantags();
246 #endif
247 break;
248 #endif
249 #if SHELL_ESCAPE
250 case A_SHELL:
251 /*
252 * !! just uses whatever is in shellcmd.
253 * Otherwise, copy cmdbuf to shellcmd,
254 * expanding any special characters ("%" or "#").
255 */
256 if (*cbuf != '!')
257 {
258 if (shellcmd != NULL)
259 free(shellcmd);
260 shellcmd = fexpand(cbuf);
261 }
262
263 if (secure)
264 break;
265 if (shellcmd == NULL)
266 lsystem("", "!done");
267 else
268 lsystem(shellcmd, "!done");
269 break;
270 #endif
271 #if PIPEC
272 case A_PIPE:
273 if (secure)
274 break;
275 (void) pipe_mark(pipec, cbuf);
276 error("|done", NULL_PARG);
277 break;
278 #endif
279 }
280 }
281
282 /*
283 * Add a character to a multi-character command.
284 */
285 static int
286 mca_char(c)
287 int c;
288 {
289 char *p;
290 int flag;
291 char buf[3];
292 PARG parg;
293
294 switch (mca)
295 {
296 case 0:
297 /*
298 * Not in a multicharacter command.
299 */
300 return (NO_MCA);
301
302 case A_PREFIX:
303 /*
304 * In the prefix of a command.
305 * This not considered a multichar command
306 * (even tho it uses cmdbuf, etc.).
307 * It is handled in the commands() switch.
308 */
309 return (NO_MCA);
310
311 case A_DIGIT:
312 /*
313 * Entering digits of a number.
314 * Terminated by a non-digit.
315 */
316 if (!((c >= '0' && c <= '9') || c == '.') &&
317 editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE|EC_NORIGHTLEFT) == A_INVALID)
318 {
319 /*
320 * Not part of the number.
321 * Treat as a normal command character.
322 */
323 number = cmd_int(&fraction);
324 mca = 0;
325 cmd_accept();
326 return (NO_MCA);
327 }
328 break;
329
330 case A_OPT_TOGGLE:
331 /*
332 * Special case for the TOGGLE_OPTION command.
333 * If the option letter which was entered is a
334 * single-char option, execute the command immediately,
335 * so user doesn't have to hit RETURN.
336 * If the first char is + or -, this indicates
337 * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE.
338 * "--" begins inputting a long option name.
339 */
340 if (optchar == '\0' && len_cmdbuf() == 0)
341 {
342 flag = (optflag & ~OPT_NO_PROMPT);
343 if (flag == OPT_NO_TOGGLE)
344 {
345 switch (c)
346 {
347 case '_':
348 /* "__" = long option name. */
349 optgetname = TRUE;
350 mca_opt_toggle();
351 return (MCA_MORE);
352 }
353 } else
354 {
355 switch (c)
356 {
357 case '+':
358 /* "-+" = UNSET. */
359 optflag = (flag == OPT_UNSET) ?
360 OPT_TOGGLE : OPT_UNSET;
361 mca_opt_toggle();
362 return (MCA_MORE);
363 case '!':
364 /* "-!" = SET */
365 optflag = (flag == OPT_SET) ?
366 OPT_TOGGLE : OPT_SET;
367 mca_opt_toggle();
368 return (MCA_MORE);
369 case CONTROL('P'):
370 optflag ^= OPT_NO_PROMPT;
371 mca_opt_toggle();
372 return (MCA_MORE);
373 case '-':
374 /* "--" = long option name. */
375 optgetname = TRUE;
376 mca_opt_toggle();
377 return (MCA_MORE);
378 }
379 }
380 }
381 if (optgetname)
382 {
383 /*
384 * We're getting a long option name.
385 * See if we've matched an option name yet.
386 * If so, display the complete name and stop
387 * accepting chars until user hits RETURN.
388 */
389 struct loption *o;
390 char *oname;
391 int lc;
392
393 if (c == '\n' || c == '\r')
394 {
395 /*
396 * When the user hits RETURN, make sure
397 * we've matched an option name, then
398 * pretend he just entered the equivalent
399 * option letter.
400 */
401 if (optchar == '\0')
402 {
403 parg.p_string = get_cmdbuf();
404 error("There is no --%s option", &parg);
405 return (MCA_DONE);
406 }
407 optgetname = FALSE;
408 cmd_reset();
409 c = optchar;
410 } else
411 {
412 if (optchar != '\0')
413 {
414 /*
415 * Already have a match for the name.
416 * Don't accept anything but erase/kill.
417 */
418 if (c == erase_char ||
419 c == erase2_char ||
420 c == kill_char)
421 return (MCA_DONE);
422 return (MCA_MORE);
423 }
424 /*
425 * Add char to cmd buffer and try to match
426 * the option name.
427 */
428 if (cmd_char(c) == CC_QUIT)
429 return (MCA_DONE);
430 p = get_cmdbuf();
431 lc = ASCII_IS_LOWER(p[0]);
432 o = findopt_name(&p, &oname, NULL);
433 if (o != NULL)
434 {
435 /*
436 * Got a match.
437 * Remember the option letter and
438 * display the full option name.
439 */
440 optchar = o->oletter;
441 if (!lc && ASCII_IS_LOWER(optchar))
442 optchar = ASCII_TO_UPPER(optchar);
443 cmd_reset();
444 mca_opt_toggle();
445 for (p = oname; *p != '\0'; p++)
446 {
447 c = *p;
448 if (!lc && ASCII_IS_LOWER(c))
449 c = ASCII_TO_UPPER(c);
450 if (cmd_char(c) != CC_OK)
451 return (MCA_DONE);
452 }
453 }
454 return (MCA_MORE);
455 }
456 } else
457 {
458 if (c == erase_char || c == erase2_char || c == kill_char)
459 break;
460 if (optchar != '\0')
461 /* We already have the option letter. */
462 break;
463 }
464
465 optchar = c;
466 if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE ||
467 single_char_option(c))
468 {
469 toggle_option(c, "", optflag);
470 return (MCA_DONE);
471 }
472 /*
473 * Display a prompt appropriate for the option letter.
474 */
475 if ((p = opt_prompt(c)) == NULL)
476 {
477 buf[0] = '-';
478 buf[1] = c;
479 buf[2] = '\0';
480 p = buf;
481 }
482 start_mca(A_OPT_TOGGLE, p, (void*)NULL, 0);
483 return (MCA_MORE);
484
485 case A_F_SEARCH:
486 case A_B_SEARCH:
487 case A_FILTER:
488 /*
489 * Special case for search commands.
490 * Certain characters as the first char of
491 * the pattern have special meaning:
492 * ! Toggle the NO_MATCH flag
493 * * Toggle the PAST_EOF flag
494 * @ Toggle the FIRST_FILE flag
495 */
496 if (len_cmdbuf() > 0)
497 /*
498 * Only works for the first char of the pattern.
499 */
500 break;
501
502 flag = 0;
503 switch (c)
504 {
505 case CONTROL('E'): /* ignore END of file */
506 case '*':
507 if (mca != A_FILTER)
508 flag = SRCH_PAST_EOF;
509 break;
510 case CONTROL('F'): /* FIRST file */
511 case '@':
512 if (mca != A_FILTER)
513 flag = SRCH_FIRST_FILE;
514 break;
515 case CONTROL('K'): /* KEEP position */
516 if (mca != A_FILTER)
517 flag = SRCH_NO_MOVE;
518 break;
519 case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */
520 flag = SRCH_NO_REGEX;
521 break;
522 case CONTROL('N'): /* NOT match */
523 case '!':
524 flag = SRCH_NO_MATCH;
525 break;
526 }
527 if (flag != 0)
528 {
529 search_type ^= flag;
530 mca_search();
531 return (MCA_MORE);
532 }
533 break;
534 }
535
536 /*
537 * Any other multicharacter command
538 * is terminated by a newline.
539 */
540 if (c == '\n' || c == '\r')
541 {
542 /*
543 * Execute the command.
544 */
545 exec_mca();
546 return (MCA_DONE);
547 }
548
549 /*
550 * Append the char to the command buffer.
551 */
552 if (cmd_char(c) == CC_QUIT)
553 /*
554 * Abort the multi-char command.
555 */
556 return (MCA_DONE);
557
558 if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
559 {
560 /*
561 * Special case for the bracket-matching commands.
562 * Execute the command after getting exactly two
563 * characters from the user.
564 */
565 exec_mca();
566 return (MCA_DONE);
567 }
568
569 /*
570 * Need another character.
571 */
572 return (MCA_MORE);
573 }
574
575 /*
576 * Discard any buffered file data.
577 */
578 static void
579 clear_buffers()
580 {
581 if (!(ch_getflags() & CH_CANSEEK))
582 return;
583 ch_flush();
584 clr_linenum();
585 #if HILITE_SEARCH
586 clr_hilite();
587 #endif
588 }
589
590 /*
591 * Make sure the screen is displayed.
592 */
593 static void
594 make_display()
595 {
596 /*
597 * If nothing is displayed yet, display starting from initial_scrpos.
598 */
599 if (empty_screen())
600 {
601 if (initial_scrpos.pos == NULL_POSITION)
602 /*
603 * {{ Maybe this should be:
604 * jump_loc(ch_zero(), jump_sline);
605 * but this behavior seems rather unexpected
606 * on the first screen. }}
607 */
608 jump_loc(ch_zero(), 1);
609 else
610 jump_loc(initial_scrpos.pos, initial_scrpos.ln);
611 } else if (screen_trashed)
612 {
613 int save_top_scroll = top_scroll;
614 int save_ignore_eoi = ignore_eoi;
615 top_scroll = 1;
616 ignore_eoi = 0;
617 if (screen_trashed == 2)
618 {
619 /* Special case used by ignore_eoi: re-open the input file
620 * and jump to the end of the file. */
621 reopen_curr_ifile();
622 jump_forw();
623 }
624 repaint();
625 top_scroll = save_top_scroll;
626 ignore_eoi = save_ignore_eoi;
627 }
628 }
629
630 /*
631 * Display the appropriate prompt.
632 */
633 static void
634 prompt()
635 {
636 register char *p;
637
638 if (ungotp != NULL && ungotp > ungot)
639 {
640 /*
641 * No prompt necessary if commands are from
642 * ungotten chars rather than from the user.
643 */
644 return;
645 }
646
647 /*
648 * Make sure the screen is displayed.
649 */
650 make_display();
651 bottompos = position(BOTTOM_PLUS_ONE);
652
653 /*
654 * If we've hit EOF on the last file and the -E flag is set, quit.
655 */
656 if (get_quit_at_eof() == OPT_ONPLUS &&
657 eof_displayed() && !(ch_getflags() & CH_HELPFILE) &&
658 next_ifile(curr_ifile) == NULL_IFILE)
659 quit(QUIT_OK);
660
661 /*
662 * If the entire file is displayed and the -F flag is set, quit.
663 */
664 if (quit_if_one_screen &&
665 entire_file_displayed() && !(ch_getflags() & CH_HELPFILE) &&
666 next_ifile(curr_ifile) == NULL_IFILE)
667 quit(QUIT_OK);
668
669 #if MSDOS_COMPILER==WIN32C
670 /*
671 * In Win32, display the file name in the window title.
672 */
673 if (!(ch_getflags() & CH_HELPFILE))
674 SetConsoleTitle(pr_expand("Less?f - %f.", 0));
675 #endif
676 /*
677 * Select the proper prompt and display it.
678 */
679 /*
680 * If the previous action was a forward movement,
681 * don't clear the bottom line of the display;
682 * just print the prompt since the forward movement guarantees
683 * that we're in the right position to display the prompt.
684 * Clearing the line could cause a problem: for example, if the last
685 * line displayed ended at the right screen edge without a newline,
686 * then clearing would clear the last displayed line rather than
687 * the prompt line.
688 */
689 if (!forw_prompt)
690 clear_bot();
691 clear_cmd();
692 forw_prompt = 0;
693 p = pr_string();
694 if (is_filtering())
695 putstr("& ");
696 if (p == NULL || *p == '\0')
697 putchr(':');
698 else
699 {
700 at_enter(AT_STANDOUT);
701 putstr(p);
702 at_exit();
703 }
704 clear_eol();
705 }
706
707 /*
708 * Display the less version message.
709 */
710 public void
711 dispversion()
712 {
713 PARG parg;
714
715 parg.p_string = version;
716 error("less %s", &parg);
717 }
718
719 /*
720 * Get command character.
721 * The character normally comes from the keyboard,
722 * but may come from ungotten characters
723 * (characters previously given to ungetcc or ungetsc).
724 */
725 public int
726 getcc()
727 {
728 if (ungotp == NULL)
729 /*
730 * Normal case: no ungotten chars, so get one from the user.
731 */
732 return (getchr());
733
734 if (ungotp > ungot)
735 /*
736 * Return the next ungotten char.
737 */
738 return (*--ungotp);
739
740 /*
741 * We have just run out of ungotten chars.
742 */
743 ungotp = NULL;
744 if (len_cmdbuf() == 0 || !empty_screen())
745 return (getchr());
746 /*
747 * Command is incomplete, so try to complete it.
748 */
749 switch (mca)
750 {
751 case A_DIGIT:
752 /*
753 * We have a number but no command. Treat as #g.
754 */
755 return ('g');
756
757 case A_F_SEARCH:
758 case A_B_SEARCH:
759 /*
760 * We have "/string" but no newline. Add the \n.
761 */
762 return ('\n');
763
764 default:
765 /*
766 * Some other incomplete command. Let user complete it.
767 */
768 return (getchr());
769 }
770 }
771
772 /*
773 * "Unget" a command character.
774 * The next getcc() will return this character.
775 */
776 public void
777 ungetcc(c)
778 int c;
779 {
780 if (ungotp == NULL)
781 ungotp = ungot;
782 if (ungotp >= ungot + sizeof(ungot))
783 {
784 error("ungetcc overflow", NULL_PARG);
785 quit(QUIT_ERROR);
786 }
787 *ungotp++ = c;
788 }
789
790 /*
791 * Unget a whole string of command characters.
792 * The next sequence of getcc()'s will return this string.
793 */
794 public void
795 ungetsc(s)
796 char *s;
797 {
798 register char *p;
799
800 for (p = s + strlen(s) - 1; p >= s; p--)
801 ungetcc(*p);
802 }
803
804 /*
805 * Search for a pattern, possibly in multiple files.
806 * If SRCH_FIRST_FILE is set, begin searching at the first file.
807 * If SRCH_PAST_EOF is set, continue the search thru multiple files.
808 */
809 static void
810 multi_search(pattern, n)
811 char *pattern;
812 int n;
813 {
814 register int nomore;
815 IFILE save_ifile;
816 int changed_file;
817
818 changed_file = 0;
819 save_ifile = save_curr_ifile();
820
821 if (search_type & SRCH_FIRST_FILE)
822 {
823 /*
824 * Start at the first (or last) file
825 * in the command line list.
826 */
827 if (search_type & SRCH_FORW)
828 nomore = edit_first();
829 else
830 nomore = edit_last();
831 if (nomore)
832 {
833 unsave_ifile(save_ifile);
834 return;
835 }
836 changed_file = 1;
837 search_type &= ~SRCH_FIRST_FILE;
838 }
839
840 for (;;)
841 {
842 n = search(search_type, pattern, n);
843 /*
844 * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared
845 * after being used once. This allows "n" to work after
846 * using a /@@ search.
847 */
848 search_type &= ~SRCH_NO_MOVE;
849 if (n == 0)
850 {
851 /*
852 * Found it.
853 */
854 unsave_ifile(save_ifile);
855 return;
856 }
857
858 if (n < 0)
859 /*
860 * Some kind of error in the search.
861 * Error message has been printed by search().
862 */
863 break;
864
865 if ((search_type & SRCH_PAST_EOF) == 0)
866 /*
867 * We didn't find a match, but we're
868 * supposed to search only one file.
869 */
870 break;
871 /*
872 * Move on to the next file.
873 */
874 if (search_type & SRCH_FORW)
875 nomore = edit_next(1);
876 else
877 nomore = edit_prev(1);
878 if (nomore)
879 break;
880 changed_file = 1;
881 }
882
883 /*
884 * Didn't find it.
885 * Print an error message if we haven't already.
886 */
887 if (n > 0)
888 error("Pattern not found", NULL_PARG);
889
890 if (changed_file)
891 {
892 /*
893 * Restore the file we were originally viewing.
894 */
895 reedit_ifile(save_ifile);
896 } else
897 {
898 unsave_ifile(save_ifile);
899 }
900 }
901
902 /*
903 * Main command processor.
904 * Accept and execute commands until a quit command.
905 */
906 public void
907 commands()
908 {
909 register int c;
910 register int action;
911 register char *cbuf;
912 int newaction;
913 int save_search_type;
914 char *extra;
915 char tbuf[2];
916 PARG parg;
917 IFILE old_ifile;
918 IFILE new_ifile;
919 char *tagfile;
920
921 search_type = SRCH_FORW;
922 wscroll = (sc_height + 1) / 2;
923 newaction = A_NOACTION;
924
925 for (;;)
926 {
927 mca = 0;
928 cmd_accept();
929 number = 0;
930 optchar = '\0';
931
932 /*
933 * See if any signals need processing.
934 */
935 if (sigs)
936 {
937 psignals();
938 if (quitting)
939 quit(QUIT_SAVED_STATUS);
940 }
941
942 /*
943 * See if window size changed, for systems that don't
944 * generate SIGWINCH.
945 */
946 check_winch();
947
948 /*
949 * Display prompt and accept a character.
950 */
951 cmd_reset();
952 prompt();
953 if (sigs)
954 continue;
955 if (newaction == A_NOACTION)
956 c = getcc();
957
958 again:
959 if (sigs)
960 continue;
961
962 if (newaction != A_NOACTION)
963 {
964 action = newaction;
965 newaction = A_NOACTION;
966 } else
967 {
968 /*
969 * If we are in a multicharacter command, call mca_char.
970 * Otherwise we call fcmd_decode to determine the
971 * action to be performed.
972 */
973 if (mca)
974 switch (mca_char(c))
975 {
976 case MCA_MORE:
977 /*
978 * Need another character.
979 */
980 c = getcc();
981 goto again;
982 case MCA_DONE:
983 /*
984 * Command has been handled by mca_char.
985 * Start clean with a prompt.
986 */
987 continue;
988 case NO_MCA:
989 /*
990 * Not a multi-char command
991 * (at least, not anymore).
992 */
993 break;
994 }
995
996 /*
997 * Decode the command character and decide what to do.
998 */
999 if (mca)
1000 {
1001 /*
1002 * We're in a multichar command.
1003 * Add the character to the command buffer
1004 * and display it on the screen.
1005 * If the user backspaces past the start
1006 * of the line, abort the command.
1007 */
1008 if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
1009 continue;
1010 cbuf = get_cmdbuf();
1011 } else
1012 {
1013 /*
1014 * Don't use cmd_char if we're starting fresh
1015 * at the beginning of a command, because we
1016 * don't want to echo the command until we know
1017 * it is a multichar command. We also don't
1018 * want erase_char/kill_char to be treated
1019 * as line editing characters.
1020 */
1021 tbuf[0] = c;
1022 tbuf[1] = '\0';
1023 cbuf = tbuf;
1024 }
1025 extra = NULL;
1026 action = fcmd_decode(cbuf, &extra);
1027 /*
1028 * If an "extra" string was returned,
1029 * process it as a string of command characters.
1030 */
1031 if (extra != NULL)
1032 ungetsc(extra);
1033 }
1034 /*
1035 * Clear the cmdbuf string.
1036 * (But not if we're in the prefix of a command,
1037 * because the partial command string is kept there.)
1038 */
1039 if (action != A_PREFIX)
1040 cmd_reset();
1041
1042 switch (action)
1043 {
1044 case A_DIGIT:
1045 /*
1046 * First digit of a number.
1047 */
1048 start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE);
1049 goto again;
1050
1051 case A_F_WINDOW:
1052 /*
1053 * Forward one window (and set the window size).
1054 */
1055 if (number > 0)
1056 swindow = (int) number;
1057 /* FALLTHRU */
1058 case A_F_SCREEN:
1059 /*
1060 * Forward one screen.
1061 */
1062 if (number <= 0)
1063 number = get_swindow();
1064 cmd_exec();
1065 if (show_attn)
1066 set_attnpos(bottompos);
1067 forward((int) number, 0, 1);
1068 break;
1069
1070 case A_B_WINDOW:
1071 /*
1072 * Backward one window (and set the window size).
1073 */
1074 if (number > 0)
1075 swindow = (int) number;
1076 /* FALLTHRU */
1077 case A_B_SCREEN:
1078 /*
1079 * Backward one screen.
1080 */
1081 if (number <= 0)
1082 number = get_swindow();
1083 cmd_exec();
1084 backward((int) number, 0, 1);
1085 break;
1086
1087 case A_F_LINE:
1088 /*
1089 * Forward N (default 1) line.
1090 */
1091 if (number <= 0)
1092 number = 1;
1093 cmd_exec();
1094 if (show_attn == OPT_ONPLUS && number > 1)
1095 set_attnpos(bottompos);
1096 forward((int) number, 0, 0);
1097 break;
1098
1099 case A_B_LINE:
1100 /*
1101 * Backward N (default 1) line.
1102 */
1103 if (number <= 0)
1104 number = 1;
1105 cmd_exec();
1106 backward((int) number, 0, 0);
1107 break;
1108
1109 case A_FF_LINE:
1110 /*
1111 * Force forward N (default 1) line.
1112 */
1113 if (number <= 0)
1114 number = 1;
1115 cmd_exec();
1116 if (show_attn == OPT_ONPLUS && number > 1)
1117 set_attnpos(bottompos);
1118 forward((int) number, 1, 0);
1119 break;
1120
1121 case A_BF_LINE:
1122 /*
1123 * Force backward N (default 1) line.
1124 */
1125 if (number <= 0)
1126 number = 1;
1127 cmd_exec();
1128 backward((int) number, 1, 0);
1129 break;
1130
1131 case A_FF_SCREEN:
1132 /*
1133 * Force forward one screen.
1134 */
1135 if (number <= 0)
1136 number = get_swindow();
1137 cmd_exec();
1138 if (show_attn == OPT_ONPLUS)
1139 set_attnpos(bottompos);
1140 forward((int) number, 1