1 /* $NetBSD: command.c,v 1.11 2004/09/01 01:46:56 chs Exp $ */
4 * Copyright (c) 1988 Mark Nudelman
5 * Copyright (c) 1988, 1993
6 * The Regents of the University of California. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 #include <sys/cdefs.h>
36 static char sccsid
[] = "@(#)command.c 8.1 (Berkeley) 6/6/93";
38 __RCSID("$NetBSD: command.c,v 1.11 2004/09/01 01:46:56 chs Exp $");
42 #include <sys/param.h>
50 #include "pathnames.h"
57 static char cmdbuf
[120]; /* Buffer for holding a multi-char command */
58 static char *cp
; /* Pointer into cmdbuf */
59 static int cmd_col
; /* Current column of the multi-char command */
60 static int longprompt
; /* if stat command instead of prompt */
61 static int mca
; /* The multicharacter command (action) */
62 static int last_mca
; /* The previous mca */
63 static int number
; /* The number typed by the user */
64 static int wsearch
; /* Search for matches (1) or non-matches (0) */
66 #define CMD_RESET cp = cmdbuf /* reset command buffer to empty */
67 #define CMD_EXEC lower_left(); flush()
69 static int cmd_erase
__P((void));
70 static int cmd_char
__P((int));
71 static int getcc
__P((void));
72 static void exec_mca
__P((void));
73 static int mca_char
__P((int));
75 /* backspace in command buffer. */
80 * backspace past beginning of the string: this usually means
86 /* erase an extra character, for the carat. */
87 if (CONTROL_CHAR(*--cp
)) {
97 /* set up the display to start a new multi-character command. */
99 start_mca(action
, prompt
)
106 cmd_col
= strlen(prompt
);
111 * process a single character of a multi-character command, such as
112 * a number, or the pattern of a search command.
120 /* in this order, in case werase == erase_char */
121 if (c
== werase_char
) {
123 while (isspace((unsigned char)cp
[-1]) && !cmd_erase());
124 while (!isspace((unsigned char)cp
[-1]) && !cmd_erase());
125 while (isspace((unsigned char)cp
[-1]) && !cmd_erase());
127 return(cp
== cmdbuf
);
129 if (c
== kill_char
) {
130 while (!cmd_erase());
134 * No room in the command buffer, or no room on the screen;
135 * {{ Could get fancy here; maybe shift the displayed line
136 * and make room for more chars, like ksh. }}
138 if (cp
>= &cmdbuf
[sizeof(cmdbuf
)-1] || cmd_col
>= sc_width
-3)
142 if (CONTROL_CHAR(c
)) {
160 * if nothing is displayed yet, display starting from line 1;
161 * if search string provided, go there instead.
163 if (position(TOP
) == NULL_POSITION
) {
164 if (forw_line((off_t
)0) == NULL_POSITION
)
166 if (!firstsearch
|| !search(1, firstsearch
, 1, 1))
169 else if (screen_trashed
)
172 /* if no -e flag and we've hit EOF on the last file, quit. */
173 if ((!quit_at_eof
|| short_file
) && hit_eof
&& curr_ac
+ 1 >= ac
)
176 /* select the proper prompt and display it. */
181 putstr(current_name
);
184 (void)snprintf(pbuf
, sizeof(pbuf
), " file %d/%d",
189 (void)snprintf(pbuf
, sizeof(pbuf
), " line %d",
193 if ((pos
= position(BOTTOM
)) != NULL_POSITION
) {
194 (void)snprintf(pbuf
, sizeof(pbuf
), " byte %lld",
197 if (!ispipe
&& (len
= ch_length())) {
198 (void)snprintf(pbuf
, sizeof(pbuf
),
199 "/%lld pct %lld%%", (long long)len
,
200 (long long)((100 * pos
) / len
));
209 putstr(current_name
);
212 putstr(": END (next file: ");
219 (pos
= position(BOTTOM
)) != NULL_POSITION
&&
220 (len
= ch_length())) {
221 (void)snprintf(pbuf
, sizeof(pbuf
), " (%lld%%)",
222 (long long)((100 * pos
) / len
));
230 /* get command character. */
236 /* left over from error() routine. */
242 if (cp
> cmdbuf
&& position(TOP
) == NULL_POSITION
) {
244 * Command is incomplete, so try to complete it.
245 * There are only two cases:
246 * 1. We have "/string" but no newline. Add the \n.
247 * 2. We have a number but no command. Treat as #g.
248 * (This is all pretty hokey.)
251 /* Not a number; must be search string */
254 /* A number; append a 'g' */
260 /* execute a multicharacter command. */
270 (void)search(1, cmdbuf
, number
, wsearch
);
273 (void)search(0, cmdbuf
, number
, wsearch
);
276 for (p
= cmdbuf
; isspace((unsigned char)*p
); ++p
);
282 /* add a character to a multi-character command. */
288 case 0: /* not in a multicharacter command. */
289 case A_PREFIX
: /* in the prefix of a command. */
293 * Entering digits of a number.
294 * Terminated by a non-digit.
296 if (!isascii(c
) || (!isdigit(c
) &&
297 c
!= erase_char
&& c
!= kill_char
&& c
!= werase_char
)) {
299 * Not part of the number.
300 * Treat as a normal command character.
303 number
= atoi(cmdbuf
);
312 * Any other multicharacter command
313 * is terminated by a newline.
315 if (c
== '\n' || c
== '\r') {
320 /* append the char to the command buffer. */
328 * Main command processor.
329 * Accept and execute commands until a quit command, then return.
338 scroll_lines
= (sc_height
+ 1) / 2;
345 * See if any signals need processing.
353 * Display prompt and accept a character.
367 * If we are in a multicharacter command, call mca_char.
368 * Otherwise we call cmd_decode to determine the
369 * action to be performed.
372 switch (mca_char(c
)) {
375 * Need another character.
381 * Command has been handled by mca_char.
382 * Start clean with a prompt.
387 * Not a multi-char command
388 * (at least, not anymore).
393 /* decode the command character and decide what to do. */
394 switch (action
= cmd_decode(c
)) {
395 case A_DIGIT
: /* first digit of a number */
396 start_mca(A_DIGIT
, ":");
398 case A_F_SCREEN
: /* forward one screen */
400 if (number
<= 0 && (number
= sc_window
) <= 0)
401 number
= sc_height
- 1;
404 case A_B_SCREEN
: /* backward one screen */
406 if (number
<= 0 && (number
= sc_window
) <= 0)
407 number
= sc_height
- 1;
410 case A_F_LINE
: /* forward N (default 1) line */
412 forward(number
<= 0 ? 1 : number
, 0);
414 case A_B_LINE
: /* backward N (default 1) line */
416 backward(number
<= 0 ? 1 : number
, 0);
418 case A_F_SCROLL
: /* forward N lines */
421 scroll_lines
= number
;
422 forward(scroll_lines
, 0);
424 case A_B_SCROLL
: /* backward N lines */
427 scroll_lines
= number
;
428 backward(scroll_lines
, 0);
430 case A_FREPAINT
: /* flush buffers and repaint */
436 case A_REPAINT
: /* repaint the screen */
440 case A_GOLINE
: /* go to line N, default 1 */
446 case A_PERCENT
: /* go to percent of file */
450 else if (number
> 100)
452 jump_percent(number
);
454 case A_GOEND
: /* go to line N, default end */
461 case A_STAT
: /* print file name, etc. */
464 case A_QUIT
: /* exit */
466 case A_F_SEARCH
: /* search for a pattern */
470 start_mca(action
, (action
==A_F_SEARCH
) ? "/" : "?");
476 * Invert the sense of the search; set wsearch
477 * to 0 and get a new character for the start
481 (action
== A_F_SEARCH
) ? "!/" : "!?");
486 case A_AGAIN_SEARCH
: /* repeat previous search */
491 (last_mca
== A_F_SEARCH
) ? "/" : "?");
494 (last_mca
== A_F_SEARCH
) ? "!/" : "!?");
496 (void)search(mca
== A_F_SEARCH
, NULL
,
499 case A_HELP
: /* help */
506 case A_FILE_LIST
: /* show list of file names */
511 case A_EXAMINE
: /* edit a new file */
513 start_mca(A_EXAMINE
, "Examine: ");
516 case A_VISUAL
: /* invoke the editor */
518 error("Cannot edit standard input");
526 case A_NEXT_FILE
: /* examine next file */
531 case A_PREV_FILE
: /* examine previous file */
536 case A_SETMARK
: /* set a mark */
539 start_mca(A_SETMARK
, "mark: ");
541 if (c
== erase_char
|| c
== kill_char
)
545 case A_GOMARK
: /* go to mark */
548 start_mca(A_GOMARK
, "goto mark: ");
550 if (c
== erase_char
|| c
== kill_char
)
556 * The command is incomplete (more chars are needed).
557 * Display the current char so the user knows what's
558 * going on and get another character.
561 start_mca(A_PREFIX
, "");
562 if (CONTROL_CHAR(c
)) {
579 static int dolinenumber
;
582 char buf
[MAXPATHLEN
* 2 + 20];
584 if (editor
== NULL
) {
585 editor
= getenv("EDITOR");
586 /* pass the line number to vi */
587 if (editor
== NULL
|| *editor
== '\0') {
594 if (dolinenumber
&& (c
= currline(MIDDLE
)))
595 (void)snprintf(buf
, sizeof(buf
), "%s +%d %s", editor
, c
,
598 (void)snprintf(buf
, sizeof(buf
), "%s %s", editor
, current_file
);
610 error("No files provided as arguments.");
613 for (width
= indx
= 0; indx
< ac
;) {
614 p
= strcmp(av
[indx
], "-") ? av
[indx
] : "stdin";
618 if (width
+ len
+ 1 >= sc_width
) {