3 * Copyright (C) 1984-2000 Mark Nudelman
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.
8 * For more information about less, or for information on how to
9 * contact the author, see the README file.
14 * User-level command processor.
18 #if MSDOS_COMPILER==WIN32C
25 extern int erase_char, kill_char;
27 extern int quit_at_eof;
28 extern int quit_if_one_screen;
34 extern int jump_sline;
37 extern int top_scroll;
38 extern int ignore_eoi;
43 extern char *every_first_cmd;
44 extern char *curr_altfilename;
45 extern char version[];
46 extern struct scrpos initial_scrpos;
47 extern IFILE curr_ifile;
48 extern void constant *ml_search;
49 extern void constant *ml_examine;
50 #if SHELL_ESCAPE || PIPEC
51 extern void constant *ml_shell;
55 extern char *editproto;
57 extern int screen_trashed; /* The screen has been overwritten */
58 extern int shift_count;
60 static char ungot[UNGOT_SIZE];
61 static char *ungotp = NULL;
63 static char *shellcmd = NULL; /* For holding last shell command for "!!" */
65 static int mca; /* The multicharacter command (action) */
66 static int search_type; /* The previous type of search */
67 static int number; /* The number typed by the user */
70 static int optgetname;
71 static POSITION bottompos;
76 static void multi_search();
79 * Move the cursor to lower left before executing a command.
80 * This looks nicer if the command takes a long time before
81 * updating the screen.
92 * Set up the display to start a new multi-character command.
95 start_mca(action, prompt, mlist, cmdflags)
104 set_mlist(mlist, cmdflags);
110 return (mca != 0 && mca != A_PREFIX);
114 * Set up the display to start a new search command.
119 if (search_type & SRCH_FORW)
126 if (search_type & SRCH_NO_MATCH)
127 cmd_putstr("Non-match ");
128 if (search_type & SRCH_FIRST_FILE)
129 cmd_putstr("First-file ");
130 if (search_type & SRCH_PAST_EOF)
131 cmd_putstr("EOF-ignore ");
132 if (search_type & SRCH_NO_MOVE)
133 cmd_putstr("Keep-pos ");
134 if (search_type & SRCH_NO_REGEX)
135 cmd_putstr("Regex-off ");
137 if (search_type & SRCH_FORW)
141 set_mlist(ml_search, 0);
145 * Set up the display to start a new toggle-option command.
154 no_prompt = (optflag & OPT_NO_PROMPT);
155 flag = (optflag & ~OPT_NO_PROMPT);
156 dash = (flag == OPT_NO_TOGGLE) ? "_" : "-";
178 * Execute a multicharacter command.
192 multi_search(cbuf, number);
196 * Skip leading spaces or + signs in the string.
198 while (*cbuf == '+' || *cbuf == ' ')
200 if (every_first_cmd != NULL)
201 free(every_first_cmd);
203 every_first_cmd = NULL;
205 every_first_cmd = save(cbuf);
208 toggle_option(optchar, cbuf, optflag);
212 match_brac(cbuf[0], cbuf[1], 1, number);
215 match_brac(cbuf[1], cbuf[0], 0, number);
222 /* If tag structure is loaded then clean it up. */
229 * !! just uses whatever is in shellcmd.
230 * Otherwise, copy cmdbuf to shellcmd,
231 * expanding any special characters ("%" or "#").
235 if (shellcmd != NULL)
237 shellcmd = fexpand(cbuf);
242 if (shellcmd == NULL)
243 lsystem("", "!done");
245 lsystem(shellcmd, "!done");
252 (void) pipe_mark(pipec, cbuf);
253 error("|done", NULL_PARG);
260 * Add a character to a multi-character command.
275 * Not in a multicharacter command.
281 * In the prefix of a command.
282 * This not considered a multichar command
283 * (even tho it uses cmdbuf, etc.).
284 * It is handled in the commands() switch.
290 * Entering digits of a number.
291 * Terminated by a non-digit.
293 if ((c < '0' || c > '9') &&
294 editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE|EC_NORIGHTLEFT) == A_INVALID)
297 * Not part of the number.
298 * Treat as a normal command character.
309 * Special case for the TOGGLE_OPTION command.
310 * If the option letter which was entered is a
311 * single-char option, execute the command immediately,
312 * so user doesn't have to hit RETURN.
313 * If the first char is + or -, this indicates
314 * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE.
315 * "--" begins inputting a long option name.
317 if (optchar == '\0' && len_cmdbuf() == 0)
319 flag = (optflag & ~OPT_NO_PROMPT);
320 if (flag == OPT_NO_TOGGLE)
325 /* "__" = long option name. */
336 optflag = (flag == OPT_UNSET) ?
337 OPT_TOGGLE : OPT_UNSET;
342 optflag = (flag == OPT_SET) ?
343 OPT_TOGGLE : OPT_SET;
347 optflag ^= OPT_NO_PROMPT;
351 /* "--" = long option name. */
361 * We're getting a long option name.
362 * See if we've matched an option name yet.
363 * If so, display the complete name and stop
364 * accepting chars until user hits RETURN.
370 if (c == '\n' || c == '\r')
373 * When the user hits RETURN, make sure
374 * we've matched an option name, then
375 * pretend he just entered the equivalent
380 parg.p_string = get_cmdbuf();
381 error("There is no --%s option", &parg);
392 * Already have a match for the name.
393 * Don't accept anything but erase/kill.
395 if (c == erase_char || c == kill_char)
400 * Add char to cmd buffer and try to match
403 if (cmd_char(c) == CC_QUIT)
407 o = findopt_name(&p, &oname, NULL);
412 * Remember the option letter and
413 * display the full option name.
415 optchar = o->oletter;
416 if (!lc && islower(optchar))
417 optchar = toupper(optchar);
420 for (p = oname; *p != '\0'; p++)
423 if (!lc && islower(c))
425 if (cmd_char(c) != CC_OK)
433 if (c == erase_char || c == kill_char)
436 /* We already have the option letter. */
441 if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE ||
442 single_char_option(c))
444 toggle_option(c, "", optflag);
448 * Display a prompt appropriate for the option letter.
450 if ((p = opt_prompt(c)) == NULL)
457 start_mca(A_OPT_TOGGLE, p, (void*)NULL, 0);
463 * Special case for search commands.
464 * Certain characters as the first char of
465 * the pattern have special meaning:
466 * ! Toggle the NO_MATCH flag
467 * * Toggle the PAST_EOF flag
468 * @ Toggle the FIRST_FILE flag
470 if (len_cmdbuf() > 0)
472 * Only works for the first char of the pattern.
482 case CONTROL('E'): /* ignore END of file */
483 flag = SRCH_PAST_EOF;
488 case CONTROL('F'): /* FIRST file */
489 flag = SRCH_FIRST_FILE;
491 case CONTROL('K'): /* KEEP position */
494 case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */
495 flag = SRCH_NO_REGEX;
497 case CONTROL('N'): /* NOT match */
499 flag = SRCH_NO_MATCH;
512 * Any other multicharacter command
513 * is terminated by a newline.
515 if (c == '\n' || c == '\r')
518 * Execute the command.
525 * Append the char to the command buffer.
527 if (cmd_char(c) == CC_QUIT)
529 * Abort the multi-char command.
533 if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
536 * Special case for the bracket-matching commands.
537 * Execute the command after getting exactly two
538 * characters from the user.
545 * Need another character.
551 * Make sure the screen is displayed.
557 * If nothing is displayed yet, display starting from initial_scrpos.
561 if (initial_scrpos.pos == NULL_POSITION)
563 * {{ Maybe this should be:
564 * jump_loc(ch_zero(), jump_sline);
565 * but this behavior seems rather unexpected
566 * on the first screen. }}
568 jump_loc(ch_zero(), 1);
570 jump_loc(initial_scrpos.pos, initial_scrpos.ln);
571 } else if (screen_trashed)
574 save_top_scroll = top_scroll;
577 top_scroll = save_top_scroll;
582 * Display the appropriate prompt.
589 if (ungotp != NULL && ungotp > ungot)
592 * No prompt necessary if commands are from
593 * ungotten chars rather than from the user.
599 * Make sure the screen is displayed.
602 bottompos = position(BOTTOM_PLUS_ONE);
605 * If the -E flag is set and we've hit EOF on the last file, quit.
607 if ((quit_at_eof == OPT_ONPLUS || quit_if_one_screen) &&
608 hit_eof && !(ch_getflags() & CH_HELPFILE) &&
609 next_ifile(curr_ifile) == NULL_IFILE)
611 quit_if_one_screen = FALSE;
612 #if 0 /* This doesn't work well because some "te"s clear the screen. */
614 * If the -e flag is set and we've hit EOF on the last file,
615 * and the file is squished (shorter than the screen), quit.
617 if (quit_at_eof && squished &&
618 next_ifile(curr_ifile) == NULL_IFILE)
622 #if MSDOS_COMPILER==WIN32C
624 * In Win32, display the file name in the window title.
626 if (!(ch_getflags() & CH_HELPFILE))
627 SetConsoleTitle(pr_expand("Less?f - %f.", 0));
630 * Select the proper prompt and display it.
645 * Display the less version message.
652 parg.p_string = version;
653 error("less %s", &parg);
657 * Get command character.
658 * The character normally comes from the keyboard,
659 * but may come from ungotten characters
660 * (characters previously given to ungetcc or ungetsc).
667 * Normal case: no ungotten chars, so get one from the user.
673 * Return the next ungotten char.
678 * We have just run out of ungotten chars.
681 if (len_cmdbuf() == 0 || !empty_screen())
684 * Command is incomplete, so try to complete it.
690 * We have a number but no command. Treat as #g.
697 * We have "/string" but no newline. Add the \n.
703 * Some other incomplete command. Let user complete it.
710 * "Unget" a command character.
711 * The next getcc() will return this character.
719 if (ungotp >= ungot + sizeof(ungot))
721 error("ungetcc overflow", NULL_PARG);
728 * Unget a whole string of command characters.
729 * The next sequence of getcc()'s will return this string.
737 for (p = s + strlen(s) - 1; p >= s; p--)
742 * Search for a pattern, possibly in multiple files.
743 * If SRCH_FIRST_FILE is set, begin searching at the first file.
744 * If SRCH_PAST_EOF is set, continue the search thru multiple files.
747 multi_search(pattern, n)
756 save_ifile = save_curr_ifile();
758 if (search_type & SRCH_FIRST_FILE)
761 * Start at the first (or last) file
762 * in the command line list.
764 if (search_type & SRCH_FORW)
765 nomore = edit_first();
767 nomore = edit_last();
770 unsave_ifile(save_ifile);
774 search_type &= ~SRCH_FIRST_FILE;
779 n = search(search_type, pattern, n);
781 * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared
782 * after being used once. This allows "n" to work after
783 * using a /@@ search.
785 search_type &= ~SRCH_NO_MOVE;
791 unsave_ifile(save_ifile);
797 * Some kind of error in the search.
798 * Error message has been printed by search().
802 if ((search_type & SRCH_PAST_EOF) == 0)
804 * We didn't find a match, but we're
805 * supposed to search only one file.
809 * Move on to the next file.
811 if (search_type & SRCH_FORW)
812 nomore = edit_next(1);
814 nomore = edit_prev(1);
822 * Print an error message if we haven't already.
825 error("Pattern not found", NULL_PARG);
830 * Restore the file we were originally viewing.
832 reedit_ifile(save_ifile);
837 * Main command processor.
838 * Accept and execute commands until a quit command.
847 int save_search_type;
855 search_type = SRCH_FORW;
856 wscroll = (sc_height + 1) / 2;
857 newaction = A_NOACTION;
867 * See if any signals need processing.
873 quit(QUIT_SAVED_STATUS);
877 * See if window size changed, for systems that don't
883 * Display prompt and accept a character.
889 if (newaction == A_NOACTION)
896 if (newaction != A_NOACTION)
899 newaction = A_NOACTION;
903 * If we are in a multicharacter command, call mca_char.
904 * Otherwise we call fcmd_decode to determine the
905 * action to be performed.
912 * Need another character.
918 * Command has been handled by mca_char.
919 * Start clean with a prompt.
924 * Not a multi-char command
925 * (at least, not anymore).
931 * Decode the command character and decide what to do.
936 * We're in a multichar command.
937 * Add the character to the command buffer
938 * and display it on the screen.
939 * If the user backspaces past the start
940 * of the line, abort the command.
942 if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
948 * Don't use cmd_char if we're starting fresh
949 * at the beginning of a command, because we
950 * don't want to echo the command until we know
951 * it is a multichar command. We also don't
952 * want erase_char/kill_char to be treated
953 * as line editing characters.
960 action = fcmd_decode(cbuf, &extra);
962 * If an "extra" string was returned,
963 * process it as a string of command characters.
969 * Clear the cmdbuf string.
970 * (But not if we're in the prefix of a command,
971 * because the partial command string is kept there.)
973 if (action != A_PREFIX)
980 * First digit of a number.
982 start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE);
987 * Forward one window (and set the window size).
994 * Forward one screen.
997 number = get_swindow();
1000 set_attnpos(bottompos);
1001 forward(number, 0, 1);
1006 * Backward one window (and set the window size).
1013 * Backward one screen.
1016 number = get_swindow();
1018 backward(number, 0, 1);
1023 * Forward N (default 1) line.
1028 if (show_attn == OPT_ONPLUS && number > 1)
1029 set_attnpos(bottompos);
1030 forward(number, 0, 0);
1035 * Backward N (default 1) line.
1040 backward(number, 0, 0);
1045 * Force forward N (default 1) line.
1050 if (show_attn == OPT_ONPLUS && number > 1)
1051 set_attnpos(bottompos);
1052 forward(number, 1, 0);
1057 * Force backward N (default 1) line.
1062 backward(number, 1, 0);
1067 * Force forward one screen.
1070 number = get_swindow();
1072 if (show_attn == OPT_ONPLUS)
1073 set_attnpos(bottompos);
1074 forward(number, 1, 0);
1079 * Forward forever, ignoring EOF.
1081 if (ch_getflags() & CH_HELPFILE)
1091 * This gets us back in "F mode" after processing
1092 * a non-abort signal (e.g. window-change).
1094 if (sigs && !ABORT_SIGS())
1095 newaction = A_F_FOREVER;
1101 * (default same as last 'd' or 'u' command).
1106 if (show_attn == OPT_ONPLUS)
1107 set_attnpos(bottompos);
1108 forward(wscroll, 0, 0);
1114 * (default same as last 'd' or 'u' command).
1119 backward(wscroll, 0, 0);
1124 * Flush buffers, then repaint screen.
1125 * Don't flush the buffers on a pipe!
1127 if (ch_getflags() & CH_CANSEEK)
1146 * Go to line N, default beginning of file.
1156 * Go to a specified percentage into the file.
1163 jump_percent(number);
1168 * Go to line N, default end of file.
1179 * Go to a specified byte position in the file.
1184 jump_line_loc((POSITION)number, jump_sline);
1189 * Print file name, etc.
1191 if (ch_getflags() & CH_HELPFILE)
1194 parg.p_string = eq_message();
1200 * Print version number, without the "@(#)".
1210 if (curr_ifile != NULL_IFILE &&
1211 ch_getflags() & CH_HELPFILE)
1214 * Quit while viewing the help file
1215 * just means return to viewing the
1218 if (edit_prev(1) == 0)
1227 * Define abbreviation for a commonly used sequence below.
1229 #define DO_SEARCH() if (number <= 0) number = 1; \
1232 multi_search((char *)NULL, number);
1237 * Search forward for a pattern.
1238 * Get the first char of the pattern.
1240 search_type = SRCH_FORW;
1249 * Search backward for a pattern.
1250 * Get the first char of the pattern.
1252 search_type = SRCH_BACK;
1259 case A_AGAIN_SEARCH:
1261 * Repeat previous search.
1266 case A_T_AGAIN_SEARCH:
1268 * Repeat previous search, multiple files.
1270 search_type |= SRCH_PAST_EOF;
1274 case A_REVERSE_SEARCH:
1276 * Repeat previous search, in reverse direction.
1278 save_search_type = search_type;
1279 search_type = SRCH_REVERSE(search_type);
1281 search_type = save_search_type;
1284 case A_T_REVERSE_SEARCH:
1286 * Repeat previous search,
1287 * multiple files in reverse direction.
1289 save_search_type = search_type;
1290 search_type = SRCH_REVERSE(search_type);
1291 search_type |= SRCH_PAST_EOF;
1293 search_type = save_search_type;
1304 if (ch_getflags() & CH_HELPFILE)
1307 (void) edit(FAKE_HELPFILE);
1313 * Edit a new file. Get the filename.
1317 error("Command not available", NULL_PARG);
1320 start_mca(A_EXAMINE, "Examine: ", ml_examine, 0);
1324 error("Command not available", NULL_PARG);
1330 * Invoke an editor on the input file.
1335 error("Command not available", NULL_PARG);
1338 if (ch_getflags() & CH_HELPFILE)
1340 if (strcmp(get_filename(curr_ifile), "-") == 0)
1342 error("Cannot edit standard input", NULL_PARG);
1345 if (curr_altfilename != NULL)
1347 error("Cannot edit file processed with LESSOPEN",
1351 start_mca(A_SHELL, "!", ml_shell, 0);
1353 * Expand the editor prototype string
1354 * and pass it to the system to execute.
1355 * (Make sure the screen is displayed so the
1356 * expansion of "+%lm" works.)
1360 lsystem(pr_expand(editproto, 0), (char*)NULL);
1363 error("Command not available", NULL_PARG);
1369 * Examine next file.
1373 error("No next file", NULL_PARG);
1378 if (edit_next(number))
1380 if (quit_at_eof && hit_eof &&
1381 !(ch_getflags() & CH_HELPFILE))
1383 parg.p_string = (number > 1) ? "(N-th) " : "";
1384 error("No %snext file", &parg);
1390 * Examine previous file.
1394 error("No previous file", NULL_PARG);
1399 if (edit_prev(number))
1401 parg.p_string = (number > 1) ? "(N-th) " : "";
1402 error("No %sprevious file", &parg);
1409 tagfile = nexttag(number);
1410 if (tagfile == NULL)
1412 error("No next tag", NULL_PARG);
1415 if (edit(tagfile) == 0)
1417 POSITION pos = tagsearch();
1418 if (pos != NULL_POSITION)
1419 jump_loc(pos, jump_sline);
1426 tagfile = prevtag(number);
1427 if (tagfile == NULL)
1429 error("No previous tag", NULL_PARG);
1432 if (edit(tagfile) == 0)
1434 POSITION pos = tagsearch();
1435 if (pos != NULL_POSITION)
1436 jump_loc(pos, jump_sline);
1442 * Examine a particular file.
1446 if (edit_index(number))
1447 error("No such file", NULL_PARG);
1451 if (ch_getflags() & CH_HELPFILE)
1453 old_ifile = curr_ifile;
1454 new_ifile = getoff_ifile(curr_ifile);
1455 if (new_ifile == NULL_IFILE)
1460 if (edit_ifile(new_ifile) != 0)
1462 reedit_ifile(old_ifile);
1465 del_ifile(old_ifile);
1469 optflag = OPT_TOGGLE;
1477 * Report a flag setting.
1479 optflag = OPT_NO_TOGGLE;
1487 * Set an initial command for new files.
1489 start_mca(A_FIRSTCMD, "+", (void*)NULL, 0);
1500 error("Command not available", NULL_PARG);
1503 start_mca(A_SHELL, "!", ml_shell, 0);
1507 error("Command not available", NULL_PARG);
1515 if (ch_getflags() & CH_HELPFILE)
1517 start_mca(A_SETMARK, "mark: ", (void*)NULL, 0);
1519 if (c == erase_char || c == kill_char ||
1520 c == '\n' || c == '\r')
1529 start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0);
1531 if (c == erase_char || c == kill_char ||
1532 c == '\n' || c == '\r')
1541 error("Command not available", NULL_PARG);
1544 start_mca(A_PIPE, "|mark: ", (void*)NULL, 0);
1546 if (c == erase_char || c == kill_char)
1548 if (c == '\n' || c == '\r')
1553 start_mca(A_PIPE, "!", ml_shell, 0);
1557 error("Command not available", NULL_PARG);
1563 start_mca(action, "Brackets: ", (void*)NULL, 0);
1569 shift_count = number;
1571 number = (shift_count > 0) ?
1572 shift_count : sc_width / 2;
1573 if (number > hshift)
1581 shift_count = number;
1583 number = (shift_count > 0) ?
1584 shift_count : sc_width / 2;
1591 * The command is incomplete (more chars are needed).
1592 * Display the current char, so the user knows
1593 * what's going on, and get another character.
1595 if (mca != A_PREFIX)
1598 start_mca(A_PREFIX, " ", (void*)NULL,