2 * Copyright (C) 1984-2012 Mark Nudelman
3 * Modified for use with illumos by Garrett D'Amore.
4 * Copyright 2014 Garrett D'Amore <garrett@damore.org>
6 * You may distribute under the terms of either the GNU General Public
7 * License or the Less License, as specified in the README file.
9 * For more information, see the README file.
13 * User-level command processor.
21 extern int erase_char
, erase2_char
, kill_char
;
22 extern volatile sig_atomic_t sigs
;
23 extern int quit_if_one_screen
;
24 extern int less_is_more
;
29 extern int jump_sline
;
32 extern int top_scroll
;
33 extern int ignore_eoi
;
37 extern off_t highest_hilite
;
38 extern char *every_first_cmd
;
39 extern char *curr_altfilename
;
40 extern char version
[];
41 extern struct scrpos initial_scrpos
;
42 extern IFILE curr_ifile
;
43 extern void *ml_search
;
44 extern void *ml_examine
;
45 extern void *ml_shell
;
47 extern char *editproto
;
48 extern int screen_trashed
; /* The screen has been overwritten */
49 extern int shift_count
;
51 extern int forw_prompt
;
53 static int mca
; /* The multicharacter command (action) */
54 static int search_type
; /* The previous type of search */
55 static off_t number
; /* The number typed by the user */
56 static long fraction
; /* The fractional part of the number */
57 static struct loption
*curropt
;
60 static int optgetname
;
61 static off_t bottompos
;
62 static int save_hshift
;
66 struct ungot
*ug_next
;
69 static struct ungot
*ungot
= NULL
;
70 static int unget_end
= 0;
72 static void multi_search(char *, int);
75 * Move the cursor to start of prompt line before executing a command.
76 * This looks nicer if the command takes a long time before
77 * updating the screen.
88 * Set up the display to start a new multi-character command.
91 start_mca(int action
, const char *prompt
, void *mlist
, int cmdflags
)
96 cmd_putstr((char *)prompt
);
97 set_mlist(mlist
, cmdflags
);
103 return (mca
!= 0 && mca
!= A_PREFIX
);
107 * Set up the display to start a new search command.
112 if (search_type
& SRCH_FILTER
)
114 else if (search_type
& SRCH_FORW
)
122 if (search_type
& SRCH_NO_MATCH
)
123 cmd_putstr("Non-match ");
124 if (search_type
& SRCH_FIRST_FILE
)
125 cmd_putstr("First-file ");
126 if (search_type
& SRCH_PAST_EOF
)
127 cmd_putstr("EOF-ignore ");
128 if (search_type
& SRCH_NO_MOVE
)
129 cmd_putstr("Keep-pos ");
130 if (search_type
& SRCH_NO_REGEX
)
131 cmd_putstr("Regex-off ");
133 if (search_type
& SRCH_FILTER
)
135 else if (search_type
& SRCH_FORW
)
139 set_mlist(ml_search
, 0);
143 * Set up the display to start a new toggle-option command.
152 no_prompt
= (optflag
& OPT_NO_PROMPT
);
153 flag
= (optflag
& ~OPT_NO_PROMPT
);
154 dash
= (flag
== OPT_NO_TOGGLE
) ? "_" : "-";
176 * Execute a multicharacter command.
189 multi_search(cbuf
, (int)number
);
192 search_type
^= SRCH_NO_MATCH
;
193 set_filter_pattern(cbuf
, search_type
);
197 * Skip leading spaces or + signs in the string.
199 while (*cbuf
== '+' || *cbuf
== ' ')
201 free(every_first_cmd
);
203 every_first_cmd
= NULL
;
205 every_first_cmd
= estrdup(cbuf
);
208 toggle_option(curropt
, opt_lower
, cbuf
, optflag
);
212 match_brac(cbuf
[0], cbuf
[1], 1, (int)number
);
215 match_brac(cbuf
[1], cbuf
[0], 0, (int)number
);
221 /* POSIX behavior, but possibly generally useful */
222 if (strlen(cbuf
) == 0) {
227 /* POSIX behavior - probably not generally useful */
228 if (less_is_more
&& (strcmp(cbuf
, "#") == 0)) {
230 error("No previous file", NULL
);
234 error("No previous file", NULL
);
241 /* If tag structure is loaded then clean it up. */
247 (void) pipe_mark(pipec
, cbuf
);
248 error("|done", NULL
);
254 * Is a character an erase or kill char?
259 return (c
== erase_char
|| c
== erase2_char
|| c
== kill_char
);
263 * Handle the first char of an option (after the initial dash).
266 mca_opt_first_char(int c
)
268 int flag
= (optflag
& ~OPT_NO_PROMPT
);
269 if (flag
== OPT_NO_TOGGLE
) {
272 /* "__" = long option name. */
281 optflag
= (flag
== OPT_UNSET
) ? OPT_TOGGLE
: OPT_UNSET
;
286 optflag
= (flag
== OPT_SET
) ? OPT_TOGGLE
: OPT_SET
;
290 optflag
^= OPT_NO_PROMPT
;
294 /* "--" = long option name. */
300 /* Char was not handled here. */
305 * Add a char to a long option name.
306 * See if we've got a match for an option name yet.
307 * If so, display the complete name and stop
308 * accepting chars until user hits RETURN.
311 mca_opt_nonfirst_char(int c
)
316 if (curropt
!= NULL
) {
318 * Already have a match for the name.
319 * Don't accept anything but erase/kill.
321 if (is_erase_char(c
))
326 * Add char to cmd buffer and try to match
329 if (cmd_char(c
) == CC_QUIT
)
332 opt_lower
= islower(p
[0]);
333 curropt
= findopt_name(&p
, &oname
, NULL
);
334 if (curropt
!= NULL
) {
337 * Remember the option and
338 * display the full option name.
342 for (p
= oname
; *p
!= '\0'; p
++) {
344 if (!opt_lower
&& islower(c
))
346 if (cmd_char(c
) != CC_OK
)
354 * Handle a char of an option toggle command.
362 * This may be a short option (single char),
363 * or one char of a long option name,
364 * or one char of the option parameter.
366 if (curropt
== NULL
&& len_cmdbuf() == 0) {
367 int ret
= mca_opt_first_char(c
);
372 /* We're getting a long option name. */
373 if (c
!= '\n' && c
!= '\r')
374 return (mca_opt_nonfirst_char(c
));
375 if (curropt
== NULL
) {
376 parg
.p_string
= get_cmdbuf();
377 error("There is no --%s option", &parg
);
383 if (is_erase_char(c
))
386 /* We're getting the option parameter. */
388 curropt
= findopt(c
);
389 if (curropt
== NULL
) {
390 parg
.p_string
= propt(c
);
391 error("There is no %s option", &parg
);
396 * If the option which was entered does not take a
397 * parameter, toggle the option immediately,
398 * so user doesn't have to hit RETURN.
400 if ((optflag
& ~OPT_NO_PROMPT
) != OPT_TOGGLE
||
401 !opt_has_param(curropt
)) {
402 toggle_option(curropt
, islower(c
), "", optflag
);
406 * Display a prompt appropriate for the option parameter.
408 start_mca(A_OPT_TOGGLE
, opt_prompt(curropt
), NULL
, 0);
413 * Handle a char of a search command.
416 mca_search_char(int c
)
421 * Certain characters as the first char of
422 * the pattern have special meaning:
423 * ! Toggle the NO_MATCH flag
424 * * Toggle the PAST_EOF flag
425 * @ Toggle the FIRST_FILE flag
427 if (len_cmdbuf() > 0)
431 case CONTROL('E'): /* ignore END of file */
434 flag
= SRCH_PAST_EOF
;
436 case CONTROL('F'): /* FIRST file */
439 flag
= SRCH_FIRST_FILE
;
441 case CONTROL('K'): /* KEEP position */
445 case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */
446 flag
= SRCH_NO_REGEX
;
448 case CONTROL('N'): /* NOT match */
450 flag
= SRCH_NO_MATCH
;
463 * Handle a character of a multi-character command.
473 * We're not in a multicharacter command.
479 * In the prefix of a command.
480 * This not considered a multichar command
481 * (even tho it uses cmdbuf, etc.).
482 * It is handled in the commands() switch.
488 * Entering digits of a number.
489 * Terminated by a non-digit.
491 if (!((c
>= '0' && c
<= '9') || c
== '.') && editchar(c
,
492 EC_PEEK
|EC_NOHISTORY
|EC_NOCOMPLETE
|EC_NORIGHTLEFT
) ==
495 * Not part of the number.
496 * End the number and treat this char
497 * as a normal command character.
499 number
= cmd_int(&fraction
);
507 ret
= mca_opt_char(c
);
515 ret
= mca_search_char(c
);
521 /* Other multicharacter command. */
526 * The multichar command is terminated by a newline.
528 if (c
== '\n' || c
== '\r') {
530 * Execute the command.
537 * Append the char to the command buffer.
539 if (cmd_char(c
) == CC_QUIT
)
541 * Abort the multi-char command.
545 if ((mca
== A_F_BRACKET
|| mca
== A_B_BRACKET
) && len_cmdbuf() >= 2) {
547 * Special case for the bracket-matching commands.
548 * Execute the command after getting exactly two
549 * characters from the user.
556 * Need another character.
562 * Discard any buffered file data.
567 if (!(ch_getflags() & CH_CANSEEK
))
575 * Make sure the screen is displayed.
581 * If nothing is displayed yet, display starting from initial_scrpos.
583 if (empty_screen()) {
584 if (initial_scrpos
.pos
== -1)
586 * {{ Maybe this should be:
587 * jump_loc(ch_zero(), jump_sline);
588 * but this behavior seems rather unexpected
589 * on the first screen. }}
591 jump_loc(ch_zero(), 1);
593 jump_loc(initial_scrpos
.pos
, initial_scrpos
.ln
);
594 } else if (screen_trashed
) {
595 int save_top_scroll
= top_scroll
;
596 int save_ignore_eoi
= ignore_eoi
;
599 if (screen_trashed
== 2) {
601 * Special case used by ignore_eoi: re-open the input
602 * file and jump to the end of the file.
608 top_scroll
= save_top_scroll
;
609 ignore_eoi
= save_ignore_eoi
;
614 * Display the appropriate prompt.
623 * No prompt necessary if commands are from
624 * ungotten chars rather than from the user.
630 * Make sure the screen is displayed.
633 bottompos
= position(BOTTOM_PLUS_ONE
);
636 * If we've hit EOF on the last file and the -E flag is set, quit.
638 if (get_quit_at_eof() == OPT_ONPLUS
&&
639 eof_displayed() && !(ch_getflags() & CH_HELPFILE
) &&
640 next_ifile(curr_ifile
) == NULL
)
644 * If the entire file is displayed and the -F flag is set, quit.
646 if (quit_if_one_screen
&&
647 entire_file_displayed() && !(ch_getflags() & CH_HELPFILE
) &&
648 next_ifile(curr_ifile
) == NULL
)
652 * Select the proper prompt and display it.
655 * If the previous action was a forward movement,
656 * don't clear the bottom line of the display;
657 * just print the prompt since the forward movement guarantees
658 * that we're in the right position to display the prompt.
659 * Clearing the line could cause a problem: for example, if the last
660 * line displayed ended at the right screen edge without a newline,
661 * then clearing would clear the last displayed line rather than
671 if (p
== NULL
|| *p
== '\0') {
674 at_enter(AT_STANDOUT
);
682 * Display the less version message.
689 parg
.p_string
= version
;
690 error("less %s", &parg
);
694 * Get command character.
695 * The character normally comes from the keyboard,
696 * but may come from ungotten characters
697 * (characters previously given to ungetcc or ungetsc).
704 * We have just run out of ungotten chars.
707 if (len_cmdbuf() == 0 || !empty_screen())
710 * Command is incomplete, so try to complete it.
715 * We have a number but no command. Treat as #g.
722 * We have "/string" but no newline. Add the \n.
728 * Some other incomplete command. Let user complete it.
736 * Normal case: no ungotten chars, so get one from the user.
742 * Return the next ungotten char.
745 struct ungot
*ug
= ungot
;
749 unget_end
= (ungot
== NULL
);
755 * "Unget" a command character.
756 * The next getcc() will return this character.
761 struct ungot
*ug
= ecalloc(1, sizeof (struct ungot
));
770 * Unget a whole string of command characters.
771 * The next sequence of getcc()'s will return this string.
778 for (p
= s
+ strlen(s
) - 1; p
>= s
; p
--)
783 * Search for a pattern, possibly in multiple files.
784 * If SRCH_FIRST_FILE is set, begin searching at the first file.
785 * If SRCH_PAST_EOF is set, continue the search thru multiple files.
788 multi_search(char *pattern
, int n
)
795 save_ifile
= save_curr_ifile();
797 if (search_type
& SRCH_FIRST_FILE
) {
799 * Start at the first (or last) file
800 * in the command line list.
802 if (search_type
& SRCH_FORW
)
803 nomore
= edit_first();
805 nomore
= edit_last();
807 unsave_ifile(save_ifile
);
811 search_type
&= ~SRCH_FIRST_FILE
;
815 n
= search(search_type
, pattern
, n
);
817 * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared
818 * after being used once. This allows "n" to work after
819 * using a /@@ search.
821 search_type
&= ~SRCH_NO_MOVE
;
826 unsave_ifile(save_ifile
);
832 * Some kind of error in the search.
833 * Error message has been printed by search().
837 if ((search_type
& SRCH_PAST_EOF
) == 0)
839 * We didn't find a match, but we're
840 * supposed to search only one file.
844 * Move on to the next file.
846 if (search_type
& SRCH_FORW
)
847 nomore
= edit_next(1);
849 nomore
= edit_prev(1);
857 * Print an error message if we haven't already.
860 error("Pattern not found", NULL
);
864 * Restore the file we were originally viewing.
866 reedit_ifile(save_ifile
);
868 unsave_ifile(save_ifile
);
873 * Forward forever, or until a highlighted line appears.
876 forw_loop(int until_hilite
)
880 if (ch_getflags() & CH_HELPFILE
)
885 curr_len
= ch_length();
886 highest_hilite
= until_hilite
? curr_len
: -1;
889 if (until_hilite
&& highest_hilite
> curr_len
) {
900 * This gets us back in "F mode" after processing
901 * a non-abort signal (e.g. window-change).
903 if (sigs
&& !ABORT_SIGS())
904 return (until_hilite
? A_F_UNTIL_HILITE
: A_F_FOREVER
);
910 * Main command processor.
911 * Accept and execute commands until a quit command.
920 int save_search_type
;
928 search_type
= SRCH_FORW
;
929 wscroll
= (sc_height
+ 1) / 2;
930 newaction
= A_NOACTION
;
939 * See if any signals need processing.
944 quit(QUIT_SAVED_STATUS
);
948 * Display prompt and accept a character.
954 if (newaction
== A_NOACTION
)
961 if (newaction
!= A_NOACTION
) {
963 newaction
= A_NOACTION
;
966 * If we are in a multicharacter command, call mca_char.
967 * Otherwise we call fcmd_decode to determine the
968 * action to be performed.
971 switch (mca_char(c
)) {
974 * Need another character.
980 * Command has been handled by mca_char.
981 * Start clean with a prompt.
986 * Not a multi-char command
987 * (at least, not anymore).
993 * Decode the command character and decide what to do.
997 * We're in a multichar command.
998 * Add the character to the command buffer
999 * and display it on the screen.
1000 * If the user backspaces past the start
1001 * of the line, abort the command.
1003 if (cmd_char(c
) == CC_QUIT
|| len_cmdbuf() == 0)
1005 cbuf
= get_cmdbuf();
1008 * Don't use cmd_char if we're starting fresh
1009 * at the beginning of a command, because we
1010 * don't want to echo the command until we know
1011 * it is a multichar command. We also don't
1012 * want erase_char/kill_char to be treated
1013 * as line editing characters.
1020 action
= fcmd_decode(cbuf
, &extra
);
1022 * If an "extra" string was returned,
1023 * process it as a string of command characters.
1029 * Clear the cmdbuf string.
1030 * (But not if we're in the prefix of a command,
1031 * because the partial command string is kept there.)
1033 if (action
!= A_PREFIX
)
1039 * First digit of a number.
1041 start_mca(A_DIGIT
, ":", (void*)NULL
, CF_QUIT_ON_ERASE
);
1046 * Forward one window (and set the window size).
1049 swindow
= (int)number
;
1053 * Forward one screen.
1056 number
= get_swindow();
1059 set_attnpos(bottompos
);
1060 forward((int)number
, 0, 1);
1065 * Backward one window (and set the window size).
1068 swindow
= (int)number
;
1072 * Backward one screen.
1075 number
= get_swindow();
1077 backward((int)number
, 0, 1);
1082 * Forward N (default 1) line.
1087 if (show_attn
== OPT_ONPLUS
&& number
> 1)
1088 set_attnpos(bottompos
);
1089 forward((int)number
, 0, 0);
1094 * Backward N (default 1) line.
1099 backward((int)number
, 0, 0);
1104 * Skip ahead one screen, and then number lines.
1107 number
= get_swindow();
1109 number
+= get_swindow();
1112 if (show_attn
== OPT_ONPLUS
)
1113 set_attnpos(bottompos
);
1114 forward((int)number
, 0, 1);
1119 * Force forward N (default 1) line.
1124 if (show_attn
== OPT_ONPLUS
&& number
> 1)
1125 set_attnpos(bottompos
);
1126 forward((int)number
, 1, 0);
1131 * Force backward N (default 1) line.
1136 backward((int)number
, 1, 0);
1141 * Force forward one screen.
1144 number
= get_swindow();
1146 if (show_attn
== OPT_ONPLUS
)
1147 set_attnpos(bottompos
);
1148 forward((int)number
, 1, 0);
1153 * Forward forever, ignoring EOF.
1155 newaction
= forw_loop(0);
1158 case A_F_UNTIL_HILITE
:
1159 newaction
= forw_loop(1);
1165 * (default same as last 'd' or 'u' command).
1168 wscroll
= (int)number
;
1170 if (show_attn
== OPT_ONPLUS
)
1171 set_attnpos(bottompos
);
1172 forward(wscroll
, 0, 0);
1178 * (default same as last 'd' or 'u' command).
1181 wscroll
= (int)number
;
1183 backward(wscroll
, 0, 0);
1188 * Flush buffers, then repaint screen.
1189 * Don't flush the buffers on a pipe!
1203 * Go to line N, default beginning of file.
1213 * Go to a specified percentage into the file.
1224 jump_percent((int)number
, fraction
);
1229 * Go to line N, default end of file.
1240 * Go to a specified byte position in the file.
1245 jump_line_loc((off_t
) number
, jump_sline
);
1250 * Print file name, etc.
1252 if (ch_getflags() & CH_HELPFILE
)
1255 parg
.p_string
= eq_message();
1261 * Print version number, without the "@(#)".
1271 if (curr_ifile
!= NULL
&&
1272 ch_getflags() & CH_HELPFILE
) {
1274 * Quit while viewing the help file
1275 * just means return to viewing the
1278 hshift
= save_hshift
;
1279 if (edit_prev(1) == 0)
1288 * Define abbreviation for a commonly used sequence below.
1290 #define DO_SEARCH() \
1291 if (number <= 0) number = 1; \
1294 multi_search(NULL, (int)number);
1299 * Search forward for a pattern.
1300 * Get the first char of the pattern.
1302 search_type
= SRCH_FORW
;
1311 * Search backward for a pattern.
1312 * Get the first char of the pattern.
1314 search_type
= SRCH_BACK
;
1322 search_type
= SRCH_FORW
| SRCH_FILTER
;
1327 case A_AGAIN_SEARCH
:
1329 * Repeat previous search.
1334 case A_T_AGAIN_SEARCH
:
1336 * Repeat previous search, multiple files.
1338 search_type
|= SRCH_PAST_EOF
;
1342 case A_REVERSE_SEARCH
:
1344 * Repeat previous search, in reverse direction.
1346 save_search_type
= search_type
;
1347 search_type
= SRCH_REVERSE(search_type
);
1349 search_type
= save_search_type
;
1352 case A_T_REVERSE_SEARCH
:
1354 * Repeat previous search,
1355 * multiple files in reverse direction.
1357 save_search_type
= search_type
;
1358 search_type
= SRCH_REVERSE(search_type
);
1359 search_type
|= SRCH_PAST_EOF
;
1361 search_type
= save_search_type
;
1372 if (ch_getflags() & CH_HELPFILE
)
1374 if (ungot
!= NULL
|| unget_end
) {
1376 ? "Invalid option -p h"
1377 : "Invalid option ++h",
1382 save_hshift
= hshift
;
1384 (void) edit(helpfile());
1389 * Edit a new file. Get the filename.
1392 error("Command not available", NULL
);
1395 start_mca(A_EXAMINE
, "Examine: ", ml_examine
, 0);
1401 * Invoke an editor on the input file.
1404 error("Command not available", NULL
);
1407 if (ch_getflags() & CH_HELPFILE
)
1409 if (strcmp(get_filename(curr_ifile
), "-") == 0) {
1410 error("Cannot edit standard input", NULL
);
1413 if (curr_altfilename
!= NULL
) {
1414 error("WARNING: This file was viewed via "
1418 * Expand the editor prototype string
1419 * and pass it to the system to execute.
1420 * (Make sure the screen is displayed so the
1421 * expansion of "+%lm" works.)
1425 lsystem(pr_expand(editproto
, 0), NULL
);
1430 * Examine next file.
1433 error("No next file", NULL
);
1438 if (edit_next((int)number
)) {
1439 if (get_quit_at_eof() && eof_displayed() &&
1440 !(ch_getflags() & CH_HELPFILE
))
1442 parg
.p_string
= (number
> 1) ? "(N-th) " : "";
1443 error("No %snext file", &parg
);
1449 * Examine previous file.
1452 error("No previous file", NULL
);
1457 if (edit_prev((int)number
)) {
1458 parg
.p_string
= (number
> 1) ? "(N-th) " : "";
1459 error("No %sprevious file", &parg
);
1467 tagfile
= nexttag((int)number
);
1468 if (tagfile
== NULL
) {
1469 error("No next tag", NULL
);
1472 if (edit(tagfile
) == 0) {
1473 off_t pos
= tagsearch();
1475 jump_loc(pos
, jump_sline
);
1482 tagfile
= prevtag((int)number
);
1483 if (tagfile
== NULL
) {
1484 error("No previous tag", NULL
);
1487 if (edit(tagfile
) == 0) {
1488 off_t pos
= tagsearch();
1490 jump_loc(pos
, jump_sline
);
1496 * Examine a particular file.
1500 if (edit_index((int)number
))
1501 error("No such file", NULL
);
1505 if (ch_getflags() & CH_HELPFILE
)
1507 old_ifile
= curr_ifile
;
1508 new_ifile
= getoff_ifile(curr_ifile
);
1509 if (new_ifile
== NULL
) {
1513 if (edit_ifile(new_ifile
) != 0) {
1514 reedit_ifile(old_ifile
);
1517 del_ifile(old_ifile
);
1521 optflag
= OPT_TOGGLE
;
1529 * Report a flag setting.
1531 optflag
= OPT_NO_TOGGLE
;
1539 * Set an initial command for new files.
1541 start_mca(A_FIRSTCMD
, "+", NULL
, 0);
1549 if (ch_getflags() & CH_HELPFILE
)
1551 start_mca(A_SETMARK
, "mark: ", (void*)NULL
, 0);
1553 if (c
== erase_char
|| c
== erase2_char
||
1554 c
== kill_char
|| c
== '\n' || c
== '\r')
1563 start_mca(A_GOMARK
, "goto mark: ", (void*)NULL
, 0);
1565 if (c
== erase_char
|| c
== erase2_char
||
1566 c
== kill_char
|| c
== '\n' || c
== '\r')
1574 error("Command not available", NULL
);
1577 start_mca(A_PIPE
, "|mark: ", (void*)NULL
, 0);
1579 if (c
== erase_char
|| c
== erase2_char
||
1582 if (c
== '\n' || c
== '\r')
1587 start_mca(A_PIPE
, "!", ml_shell
, 0);
1593 start_mca(action
, "Brackets: ", (void*)NULL
, 0);
1599 shift_count
= number
;
1601 number
= (shift_count
> 0) ?
1602 shift_count
: sc_width
/ 2;
1603 if (number
> hshift
)
1611 shift_count
= number
;
1613 number
= (shift_count
> 0) ?
1614 shift_count
: sc_width
/ 2;
1621 * The command is incomplete (more chars are needed).
1622 * Display the current char, so the user knows
1623 * what's going on, and get another character.
1625 if (mca
!= A_PREFIX
) {
1627 start_mca(A_PREFIX
, " ", (void*)NULL
,