#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)command.c 8.1 (Berkeley) 6/6/93";
#else
__RCSID("$NetBSD: command.c,v 1.12 2009/01/24 13:58:21 tsutsui Exp $");
#endif
#endif
#include <sys/param.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include "less.h"
#include "pathnames.h"
#include "extern.h"
#define NO_MCA 0
#define MCA_DONE 1
#define MCA_MORE 2
static char cmdbuf[120];
static char *cp;
static int cmd_col;
static int longprompt;
static int mca;
static int last_mca;
static int number;
static int wsearch;
#define CMD_RESET cp = cmdbuf
#define CMD_EXEC lower_left(); flush()
static int cmd_erase __P((void));
static int cmd_char __P((int));
static int getcc __P((void));
static void exec_mca __P((void));
static int mca_char __P((int));
static int
cmd_erase()
{
if (cp == cmdbuf)
return(1);
if (CONTROL_CHAR(*--cp)) {
backspace();
--cmd_col;
}
backspace();
--cmd_col;
return(0);
}
void
start_mca(action, prompt)
int action;
char *prompt;
{
lower_left();
clear_eol();
putstr(prompt);
cmd_col = strlen(prompt);
mca = action;
}
static int
cmd_char(c)
int c;
{
if (c == erase_char)
return(cmd_erase());
if (c == werase_char) {
if (cp > cmdbuf) {
while (isspace((unsigned char)cp[-1]) && !cmd_erase());
while (!isspace((unsigned char)cp[-1]) && !cmd_erase());
while (isspace((unsigned char)cp[-1]) && !cmd_erase());
}
return(cp == cmdbuf);
}
if (c == kill_char) {
while (!cmd_erase());
return(1);
}
if (cp >= &cmdbuf[sizeof(cmdbuf)-1] || cmd_col >= sc_width-3)
bell();
else {
*cp++ = c;
if (CONTROL_CHAR(c)) {
putchr('^');
cmd_col++;
c = CARAT_CHAR(c);
}
putchr(c);
cmd_col++;
}
return(0);
}
int
prompt()
{
off_t len, pos;
char pbuf[40];
if (position(TOP) == NULL_POSITION) {
if (forw_line((off_t)0) == NULL_POSITION)
return(0);
if (!firstsearch || !search(1, firstsearch, 1, 1))
jump_back(1);
}
else if (screen_trashed)
repaint();
if ((!quit_at_eof || short_file) && hit_eof && curr_ac + 1 >= ac)
quit();
lower_left();
clear_eol();
if (longprompt) {
so_enter();
putstr(current_name);
putstr(":");
if (!ispipe) {
(void)snprintf(pbuf, sizeof(pbuf), " file %d/%d",
curr_ac + 1, ac);
putstr(pbuf);
}
if (linenums) {
(void)snprintf(pbuf, sizeof(pbuf), " line %d",
currline(BOTTOM));
putstr(pbuf);
}
if ((pos = position(BOTTOM)) != NULL_POSITION) {
(void)snprintf(pbuf, sizeof(pbuf), " byte %lld",
(long long)pos);
putstr(pbuf);
if (!ispipe && (len = ch_length())) {
(void)snprintf(pbuf, sizeof(pbuf),
"/%lld pct %lld%%", (long long)len,
(long long)((100 * pos) / len));
putstr(pbuf);
}
}
so_exit();
longprompt = 0;
}
else {
so_enter();
putstr(current_name);
if (hit_eof)
if (next_name) {
putstr(": END (next file: ");
putstr(next_name);
putstr(")");
}
else
putstr(": END");
else if (!ispipe &&
(pos = position(BOTTOM)) != NULL_POSITION &&
(len = ch_length())) {
(void)snprintf(pbuf, sizeof(pbuf), " (%lld%%)",
(long long)((100 * pos) / len));
putstr(pbuf);
}
so_exit();
}
return(1);
}
static int
getcc()
{
int ch;
if (cmdstack) {
ch = cmdstack;
cmdstack = 0;
return(ch);
}
if (cp > cmdbuf && position(TOP) == NULL_POSITION) {
if (mca != A_DIGIT)
return('\n');
else
return('g');
}
return(getchr());
}
static void
exec_mca()
{
char *p;
*cp = '\0';
CMD_EXEC;
switch (mca) {
case A_F_SEARCH:
(void)search(1, cmdbuf, number, wsearch);
break;
case A_B_SEARCH:
(void)search(0, cmdbuf, number, wsearch);
break;
case A_EXAMINE:
for (p = cmdbuf; isspace((unsigned char)*p); ++p);
(void)edit(glob(p));
break;
}
}
static int
mca_char(c)
int c;
{
switch (mca) {
case 0:
case A_PREFIX:
return(NO_MCA);
case A_DIGIT:
if (!isascii(c) || (!isdigit(c) &&
c != erase_char && c != kill_char && c != werase_char)) {
*cp = '\0';
number = atoi(cmdbuf);
CMD_RESET;
mca = 0;
return(NO_MCA);
}
break;
}
if (c == '\n' || c == '\r') {
exec_mca();
return(MCA_DONE);
}
if (cmd_char(c))
return(MCA_DONE);
return(MCA_MORE);
}
void
commands()
{
int c;
int action;
last_mca = 0;
scroll_lines = (sc_height + 1) / 2;
for (;;) {
mca = 0;
number = 0;
if (sigs) {
psignals();
if (quitting)
quit();
}
CMD_RESET;
if (!prompt()) {
next_file(1);
continue;
}
noprefix();
c = getcc();
again: if (sigs)
continue;
if (mca)
switch (mca_char(c)) {
case MCA_MORE:
c = getcc();
goto again;
case MCA_DONE:
continue;
case NO_MCA:
break;
}
switch (action = cmd_decode(c)) {
case A_DIGIT:
start_mca(A_DIGIT, ":");
goto again;
case A_F_SCREEN:
CMD_EXEC;
if (number <= 0 && (number = sc_window) <= 0)
number = sc_height - 1;
forward(number, 1);
break;
case A_B_SCREEN:
CMD_EXEC;
if (number <= 0 && (number = sc_window) <= 0)
number = sc_height - 1;
backward(number, 1);
break;
case A_F_LINE:
CMD_EXEC;
forward(number <= 0 ? 1 : number, 0);
break;
case A_B_LINE:
CMD_EXEC;
backward(number <= 0 ? 1 : number, 0);
break;
case A_F_SCROLL:
CMD_EXEC;
if (number > 0)
scroll_lines = number;
forward(scroll_lines, 0);
break;
case A_B_SCROLL:
CMD_EXEC;
if (number > 0)
scroll_lines = number;
backward(scroll_lines, 0);
break;
case A_FREPAINT:
if (!ispipe) {
ch_init(0, 0);
clr_linenum();
}
case A_REPAINT:
CMD_EXEC;
repaint();
break;
case A_GOLINE:
CMD_EXEC;
if (number <= 0)
number = 1;
jump_back(number);
break;
case A_PERCENT:
CMD_EXEC;
if (number < 0)
number = 0;
else if (number > 100)
number = 100;
jump_percent(number);
break;
case A_GOEND:
CMD_EXEC;
if (number <= 0)
jump_forw();
else
jump_back(number);
break;
case A_STAT:
longprompt = 1;
continue;
case A_QUIT:
quit();
case A_F_SEARCH:
case A_B_SEARCH:
if (number <= 0)
number = 1;
start_mca(action, (action==A_F_SEARCH) ? "/" : "?");
last_mca = mca;
wsearch = 1;
c = getcc();
if (c == '!') {
start_mca(action,
(action == A_F_SEARCH) ? "!/" : "!?");
wsearch = 0;
c = getcc();
}
goto again;
case A_AGAIN_SEARCH:
if (number <= 0)
number = 1;
if (wsearch)
start_mca(last_mca,
(last_mca == A_F_SEARCH) ? "/" : "?");
else
start_mca(last_mca,
(last_mca == A_F_SEARCH) ? "!/" : "!?");
CMD_EXEC;
(void)search(mca == A_F_SEARCH, NULL,
number, wsearch);
break;
case A_HELP:
lower_left();
clear_eol();
putstr("help");
CMD_EXEC;
help();
break;
case A_FILE_LIST:
CMD_EXEC;
showlist();
repaint();
break;
case A_EXAMINE:
CMD_RESET;
start_mca(A_EXAMINE, "Examine: ");
c = getcc();
goto again;
case A_VISUAL:
if (ispipe) {
error("Cannot edit standard input");
break;
}
CMD_EXEC;
editfile();
ch_init(0, 0);
clr_linenum();
break;
case A_NEXT_FILE:
if (number <= 0)
number = 1;
next_file(number);
break;
case A_PREV_FILE:
if (number <= 0)
number = 1;
prev_file(number);
break;
case A_SETMARK:
lower_left();
clear_eol();
start_mca(A_SETMARK, "mark: ");
c = getcc();
if (c == erase_char || c == kill_char)
break;
setmark(c);
break;
case A_GOMARK:
lower_left();
clear_eol();
start_mca(A_GOMARK, "goto mark: ");
c = getcc();
if (c == erase_char || c == kill_char)
break;
gomark(c);
break;
case A_PREFIX:
if (mca != A_PREFIX)
start_mca(A_PREFIX, "");
if (CONTROL_CHAR(c)) {
putchr('^');
c = CARAT_CHAR(c);
}
putchr(c);
c = getcc();
goto again;
default:
bell();
break;
}
}
}
void
editfile()
{
static int dolinenumber;
static char *editor;
int c;
char buf[MAXPATHLEN * 2 + 20];
if (editor == NULL) {
editor = getenv("EDITOR");
if (editor == NULL || *editor == '\0') {
editor = _PATH_VI;
dolinenumber = 1;
}
else
dolinenumber = 0;
}
if (dolinenumber && (c = currline(MIDDLE)))
(void)snprintf(buf, sizeof(buf), "%s +%d %s", editor, c,
current_file);
else
(void)snprintf(buf, sizeof(buf), "%s %s", editor, current_file);
lsystem(buf);
}
void
showlist()
{
int indx, width;
int len;
char *p;
if (ac <= 0) {
error("No files provided as arguments.");
return;
}
for (width = indx = 0; indx < ac;) {
p = strcmp(av[indx], "-") ? av[indx] : "stdin";
len = strlen(p) + 1;
if (curr_ac == indx)
len += 2;
if (width + len + 1 >= sc_width) {
if (!width) {
if (curr_ac == indx)
putchr('[');
putstr(p);
if (curr_ac == indx)
putchr(']');
++indx;
}
width = 0;
putchr('\n');
continue;
}
if (width)
putchr(' ');
if (curr_ac == indx)
putchr('[');
putstr(p);
if (curr_ac == indx)
putchr(']');
width += len;
++indx;
}
putchr('\n');
error(NULL);
}