29d7b190c2
compiler-time type checking, and also causes problems for targets where multiple incompatible calling conventions may be selected based on argument types. This change switches less(1) to ANSI prototypes. While there, we also remove use of "register", and attempt to use "const" a bit better now that the compiler can check argument types. Reviewed by: cem, emaste MFC after: 3 weeks Sponsored by: DARPA, AFRL Differential Revision: https://reviews.freebsd.org/D10152
806 lines
18 KiB
C
806 lines
18 KiB
C
/*
|
|
* Copyright (C) 1984-2015 Mark Nudelman
|
|
*
|
|
* You may distribute under the terms of either the GNU General Public
|
|
* License or the Less License, as specified in the README file.
|
|
*
|
|
* For more information, see the README file.
|
|
*/
|
|
|
|
|
|
/*
|
|
* Routines to decode user commands.
|
|
*
|
|
* This is all table driven.
|
|
* A command table is a sequence of command descriptors.
|
|
* Each command descriptor is a sequence of bytes with the following format:
|
|
* <c1><c2>...<cN><0><action>
|
|
* The characters c1,c2,...,cN are the command string; that is,
|
|
* the characters which the user must type.
|
|
* It is terminated by a null <0> byte.
|
|
* The byte after the null byte is the action code associated
|
|
* with the command string.
|
|
* If an action byte is OR-ed with A_EXTRA, this indicates
|
|
* that the option byte is followed by an extra string.
|
|
*
|
|
* There may be many command tables.
|
|
* The first (default) table is built-in.
|
|
* Other tables are read in from "lesskey" files.
|
|
* All the tables are linked together and are searched in order.
|
|
*/
|
|
|
|
#include "less.h"
|
|
#include "cmd.h"
|
|
#include "lesskey.h"
|
|
|
|
extern int erase_char, erase2_char, kill_char;
|
|
extern int secure;
|
|
|
|
#define SK(k) \
|
|
SK_SPECIAL_KEY, (k), 6, 1, 1, 1
|
|
/*
|
|
* Command table is ordered roughly according to expected
|
|
* frequency of use, so the common commands are near the beginning.
|
|
*/
|
|
|
|
static unsigned char cmdtable[] =
|
|
{
|
|
'\r',0, A_F_LINE,
|
|
'\n',0, A_F_LINE,
|
|
'e',0, A_F_LINE,
|
|
'j',0, A_F_LINE,
|
|
SK(SK_DOWN_ARROW),0, A_F_LINE,
|
|
CONTROL('E'),0, A_F_LINE,
|
|
CONTROL('N'),0, A_F_LINE,
|
|
'k',0, A_B_LINE,
|
|
'y',0, A_B_LINE,
|
|
CONTROL('Y'),0, A_B_LINE,
|
|
SK(SK_CONTROL_K),0, A_B_LINE,
|
|
CONTROL('P'),0, A_B_LINE,
|
|
SK(SK_UP_ARROW),0, A_B_LINE,
|
|
'J',0, A_FF_LINE,
|
|
'K',0, A_BF_LINE,
|
|
'Y',0, A_BF_LINE,
|
|
'd',0, A_F_SCROLL,
|
|
CONTROL('D'),0, A_F_SCROLL,
|
|
'u',0, A_B_SCROLL,
|
|
CONTROL('U'),0, A_B_SCROLL,
|
|
' ',0, A_F_SCREEN,
|
|
'f',0, A_F_SCREEN,
|
|
CONTROL('F'),0, A_F_SCREEN,
|
|
CONTROL('V'),0, A_F_SCREEN,
|
|
SK(SK_PAGE_DOWN),0, A_F_SCREEN,
|
|
'b',0, A_B_SCREEN,
|
|
CONTROL('B'),0, A_B_SCREEN,
|
|
ESC,'v',0, A_B_SCREEN,
|
|
SK(SK_PAGE_UP),0, A_B_SCREEN,
|
|
'z',0, A_F_WINDOW,
|
|
'w',0, A_B_WINDOW,
|
|
ESC,' ',0, A_FF_SCREEN,
|
|
'F',0, A_F_FOREVER,
|
|
ESC,'F',0, A_F_UNTIL_HILITE,
|
|
'R',0, A_FREPAINT,
|
|
'r',0, A_REPAINT,
|
|
CONTROL('R'),0, A_REPAINT,
|
|
CONTROL('L'),0, A_REPAINT,
|
|
ESC,'u',0, A_UNDO_SEARCH,
|
|
'g',0, A_GOLINE,
|
|
SK(SK_HOME),0, A_GOLINE,
|
|
'<',0, A_GOLINE,
|
|
ESC,'<',0, A_GOLINE,
|
|
'p',0, A_PERCENT,
|
|
'%',0, A_PERCENT,
|
|
ESC,'[',0, A_LSHIFT,
|
|
ESC,']',0, A_RSHIFT,
|
|
ESC,'(',0, A_LSHIFT,
|
|
ESC,')',0, A_RSHIFT,
|
|
SK(SK_RIGHT_ARROW),0, A_RSHIFT,
|
|
SK(SK_LEFT_ARROW),0, A_LSHIFT,
|
|
'{',0, A_F_BRACKET|A_EXTRA, '{','}',0,
|
|
'}',0, A_B_BRACKET|A_EXTRA, '{','}',0,
|
|
'(',0, A_F_BRACKET|A_EXTRA, '(',')',0,
|
|
')',0, A_B_BRACKET|A_EXTRA, '(',')',0,
|
|
'[',0, A_F_BRACKET|A_EXTRA, '[',']',0,
|
|
']',0, A_B_BRACKET|A_EXTRA, '[',']',0,
|
|
ESC,CONTROL('F'),0, A_F_BRACKET,
|
|
ESC,CONTROL('B'),0, A_B_BRACKET,
|
|
'G',0, A_GOEND,
|
|
ESC,'G',0, A_GOEND_BUF,
|
|
ESC,'>',0, A_GOEND,
|
|
'>',0, A_GOEND,
|
|
SK(SK_END),0, A_GOEND,
|
|
'P',0, A_GOPOS,
|
|
|
|
'0',0, A_DIGIT,
|
|
'1',0, A_DIGIT,
|
|
'2',0, A_DIGIT,
|
|
'3',0, A_DIGIT,
|
|
'4',0, A_DIGIT,
|
|
'5',0, A_DIGIT,
|
|
'6',0, A_DIGIT,
|
|
'7',0, A_DIGIT,
|
|
'8',0, A_DIGIT,
|
|
'9',0, A_DIGIT,
|
|
'.',0, A_DIGIT,
|
|
|
|
'=',0, A_STAT,
|
|
CONTROL('G'),0, A_STAT,
|
|
':','f',0, A_STAT,
|
|
'/',0, A_F_SEARCH,
|
|
'?',0, A_B_SEARCH,
|
|
ESC,'/',0, A_F_SEARCH|A_EXTRA, '*',0,
|
|
ESC,'?',0, A_B_SEARCH|A_EXTRA, '*',0,
|
|
'n',0, A_AGAIN_SEARCH,
|
|
ESC,'n',0, A_T_AGAIN_SEARCH,
|
|
'N',0, A_REVERSE_SEARCH,
|
|
ESC,'N',0, A_T_REVERSE_SEARCH,
|
|
'&',0, A_FILTER,
|
|
'm',0, A_SETMARK,
|
|
'\'',0, A_GOMARK,
|
|
CONTROL('X'),CONTROL('X'),0, A_GOMARK,
|
|
'E',0, A_EXAMINE,
|
|
':','e',0, A_EXAMINE,
|
|
CONTROL('X'),CONTROL('V'),0, A_EXAMINE,
|
|
':','n',0, A_NEXT_FILE,
|
|
':','p',0, A_PREV_FILE,
|
|
't',0, A_NEXT_TAG,
|
|
'T',0, A_PREV_TAG,
|
|
':','x',0, A_INDEX_FILE,
|
|
':','d',0, A_REMOVE_FILE,
|
|
'-',0, A_OPT_TOGGLE,
|
|
':','t',0, A_OPT_TOGGLE|A_EXTRA, 't',0,
|
|
's',0, A_OPT_TOGGLE|A_EXTRA, 'o',0,
|
|
'_',0, A_DISP_OPTION,
|
|
'|',0, A_PIPE,
|
|
'v',0, A_VISUAL,
|
|
'!',0, A_SHELL,
|
|
'+',0, A_FIRSTCMD,
|
|
|
|
'H',0, A_HELP,
|
|
'h',0, A_HELP,
|
|
SK(SK_F1),0, A_HELP,
|
|
'V',0, A_VERSION,
|
|
'q',0, A_QUIT,
|
|
'Q',0, A_QUIT,
|
|
':','q',0, A_QUIT,
|
|
':','Q',0, A_QUIT,
|
|
'Z','Z',0, A_QUIT
|
|
};
|
|
|
|
static unsigned char edittable[] =
|
|
{
|
|
'\t',0, EC_F_COMPLETE, /* TAB */
|
|
'\17',0, EC_B_COMPLETE, /* BACKTAB */
|
|
SK(SK_BACKTAB),0, EC_B_COMPLETE, /* BACKTAB */
|
|
ESC,'\t',0, EC_B_COMPLETE, /* ESC TAB */
|
|
CONTROL('L'),0, EC_EXPAND, /* CTRL-L */
|
|
CONTROL('V'),0, EC_LITERAL, /* BACKSLASH */
|
|
CONTROL('A'),0, EC_LITERAL, /* BACKSLASH */
|
|
ESC,'l',0, EC_RIGHT, /* ESC l */
|
|
SK(SK_RIGHT_ARROW),0, EC_RIGHT, /* RIGHTARROW */
|
|
ESC,'h',0, EC_LEFT, /* ESC h */
|
|
SK(SK_LEFT_ARROW),0, EC_LEFT, /* LEFTARROW */
|
|
ESC,'b',0, EC_W_LEFT, /* ESC b */
|
|
ESC,SK(SK_LEFT_ARROW),0, EC_W_LEFT, /* ESC LEFTARROW */
|
|
SK(SK_CTL_LEFT_ARROW),0, EC_W_LEFT, /* CTRL-LEFTARROW */
|
|
ESC,'w',0, EC_W_RIGHT, /* ESC w */
|
|
ESC,SK(SK_RIGHT_ARROW),0, EC_W_RIGHT, /* ESC RIGHTARROW */
|
|
SK(SK_CTL_RIGHT_ARROW),0, EC_W_RIGHT, /* CTRL-RIGHTARROW */
|
|
ESC,'i',0, EC_INSERT, /* ESC i */
|
|
SK(SK_INSERT),0, EC_INSERT, /* INSERT */
|
|
ESC,'x',0, EC_DELETE, /* ESC x */
|
|
SK(SK_DELETE),0, EC_DELETE, /* DELETE */
|
|
ESC,'X',0, EC_W_DELETE, /* ESC X */
|
|
ESC,SK(SK_DELETE),0, EC_W_DELETE, /* ESC DELETE */
|
|
SK(SK_CTL_DELETE),0, EC_W_DELETE, /* CTRL-DELETE */
|
|
SK(SK_CTL_BACKSPACE),0, EC_W_BACKSPACE, /* CTRL-BACKSPACE */
|
|
ESC,'\b',0, EC_W_BACKSPACE, /* ESC BACKSPACE */
|
|
ESC,'0',0, EC_HOME, /* ESC 0 */
|
|
SK(SK_HOME),0, EC_HOME, /* HOME */
|
|
ESC,'$',0, EC_END, /* ESC $ */
|
|
SK(SK_END),0, EC_END, /* END */
|
|
ESC,'k',0, EC_UP, /* ESC k */
|
|
SK(SK_UP_ARROW),0, EC_UP, /* UPARROW */
|
|
ESC,'j',0, EC_DOWN, /* ESC j */
|
|
SK(SK_DOWN_ARROW),0, EC_DOWN, /* DOWNARROW */
|
|
CONTROL('G'),0, EC_ABORT, /* CTRL-G */
|
|
};
|
|
|
|
/*
|
|
* Structure to support a list of command tables.
|
|
*/
|
|
struct tablelist
|
|
{
|
|
struct tablelist *t_next;
|
|
char *t_start;
|
|
char *t_end;
|
|
};
|
|
|
|
/*
|
|
* List of command tables and list of line-edit tables.
|
|
*/
|
|
static struct tablelist *list_fcmd_tables = NULL;
|
|
static struct tablelist *list_ecmd_tables = NULL;
|
|
static struct tablelist *list_var_tables = NULL;
|
|
static struct tablelist *list_sysvar_tables = NULL;
|
|
|
|
|
|
/*
|
|
* Expand special key abbreviations in a command table.
|
|
*/
|
|
static void
|
|
expand_special_keys(char *table, int len)
|
|
{
|
|
char *fm;
|
|
char *to;
|
|
int a;
|
|
char *repl;
|
|
int klen;
|
|
|
|
for (fm = table; fm < table + len; )
|
|
{
|
|
/*
|
|
* Rewrite each command in the table with any
|
|
* special key abbreviations expanded.
|
|
*/
|
|
for (to = fm; *fm != '\0'; )
|
|
{
|
|
if (*fm != SK_SPECIAL_KEY)
|
|
{
|
|
*to++ = *fm++;
|
|
continue;
|
|
}
|
|
/*
|
|
* After SK_SPECIAL_KEY, next byte is the type
|
|
* of special key (one of the SK_* contants),
|
|
* and the byte after that is the number of bytes,
|
|
* N, reserved by the abbreviation (including the
|
|
* SK_SPECIAL_KEY and key type bytes).
|
|
* Replace all N bytes with the actual bytes
|
|
* output by the special key on this terminal.
|
|
*/
|
|
repl = special_key_str(fm[1]);
|
|
klen = fm[2] & 0377;
|
|
fm += klen;
|
|
if (repl == NULL || (int) strlen(repl) > klen)
|
|
repl = "\377";
|
|
while (*repl != '\0')
|
|
*to++ = *repl++;
|
|
}
|
|
*to++ = '\0';
|
|
/*
|
|
* Fill any unused bytes between end of command and
|
|
* the action byte with A_SKIP.
|
|
*/
|
|
while (to <= fm)
|
|
*to++ = A_SKIP;
|
|
fm++;
|
|
a = *fm++ & 0377;
|
|
if (a & A_EXTRA)
|
|
{
|
|
while (*fm++ != '\0')
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize the command lists.
|
|
*/
|
|
public void
|
|
init_cmds(void)
|
|
{
|
|
/*
|
|
* Add the default command tables.
|
|
*/
|
|
add_fcmd_table((char*)cmdtable, sizeof(cmdtable));
|
|
add_ecmd_table((char*)edittable, sizeof(edittable));
|
|
#if USERFILE
|
|
/*
|
|
* For backwards compatibility,
|
|
* try to add tables in the OLD system lesskey file.
|
|
*/
|
|
#ifdef BINDIR
|
|
add_hometable(NULL, BINDIR "/.sysless", 1);
|
|
#endif
|
|
/*
|
|
* Try to add the tables in the system lesskey file.
|
|
*/
|
|
add_hometable("LESSKEY_SYSTEM", LESSKEYFILE_SYS, 1);
|
|
/*
|
|
* Try to add the tables in the standard lesskey file "$HOME/.less".
|
|
*/
|
|
add_hometable("LESSKEY", LESSKEYFILE, 0);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Add a command table.
|
|
*/
|
|
static int
|
|
add_cmd_table(struct tablelist **tlist, char *buf, int len)
|
|
{
|
|
struct tablelist *t;
|
|
|
|
if (len == 0)
|
|
return (0);
|
|
/*
|
|
* Allocate a tablelist structure, initialize it,
|
|
* and link it into the list of tables.
|
|
*/
|
|
if ((t = (struct tablelist *)
|
|
calloc(1, sizeof(struct tablelist))) == NULL)
|
|
{
|
|
return (-1);
|
|
}
|
|
expand_special_keys(buf, len);
|
|
t->t_start = buf;
|
|
t->t_end = buf + len;
|
|
t->t_next = *tlist;
|
|
*tlist = t;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Add a command table.
|
|
*/
|
|
public void
|
|
add_fcmd_table(char *buf, int len)
|
|
{
|
|
if (add_cmd_table(&list_fcmd_tables, buf, len) < 0)
|
|
error("Warning: some commands disabled", NULL_PARG);
|
|
}
|
|
|
|
/*
|
|
* Add an editing command table.
|
|
*/
|
|
public void
|
|
add_ecmd_table(char *buf, int len)
|
|
{
|
|
if (add_cmd_table(&list_ecmd_tables, buf, len) < 0)
|
|
error("Warning: some edit commands disabled", NULL_PARG);
|
|
}
|
|
|
|
/*
|
|
* Add an environment variable table.
|
|
*/
|
|
static void
|
|
add_var_table(struct tablelist **tlist, char *buf, int len)
|
|
{
|
|
if (add_cmd_table(tlist, buf, len) < 0)
|
|
error("Warning: environment variables from lesskey file unavailable", NULL_PARG);
|
|
}
|
|
|
|
/*
|
|
* Search a single command table for the command string in cmd.
|
|
*/
|
|
static int
|
|
cmd_search(char *cmd, char *table, char *endtable, char **sp)
|
|
{
|
|
char *p;
|
|
char *q;
|
|
int a;
|
|
|
|
*sp = NULL;
|
|
for (p = table, q = cmd; p < endtable; p++, q++)
|
|
{
|
|
if (*p == *q)
|
|
{
|
|
/*
|
|
* Current characters match.
|
|
* If we're at the end of the string, we've found it.
|
|
* Return the action code, which is the character
|
|
* after the null at the end of the string
|
|
* in the command table.
|
|
*/
|
|
if (*p == '\0')
|
|
{
|
|
a = *++p & 0377;
|
|
while (a == A_SKIP)
|
|
a = *++p & 0377;
|
|
if (a == A_END_LIST)
|
|
{
|
|
/*
|
|
* We get here only if the original
|
|
* cmd string passed in was empty ("").
|
|
* I don't think that can happen,
|
|
* but just in case ...
|
|
*/
|
|
return (A_UINVALID);
|
|
}
|
|
/*
|
|
* Check for an "extra" string.
|
|
*/
|
|
if (a & A_EXTRA)
|
|
{
|
|
*sp = ++p;
|
|
a &= ~A_EXTRA;
|
|
}
|
|
return (a);
|
|
}
|
|
} else if (*q == '\0')
|
|
{
|
|
/*
|
|
* Hit the end of the user's command,
|
|
* but not the end of the string in the command table.
|
|
* The user's command is incomplete.
|
|
*/
|
|
return (A_PREFIX);
|
|
} else
|
|
{
|
|
/*
|
|
* Not a match.
|
|
* Skip ahead to the next command in the
|
|
* command table, and reset the pointer
|
|
* to the beginning of the user's command.
|
|
*/
|
|
if (*p == '\0' && p[1] == A_END_LIST)
|
|
{
|
|
/*
|
|
* A_END_LIST is a special marker that tells
|
|
* us to abort the cmd search.
|
|
*/
|
|
return (A_UINVALID);
|
|
}
|
|
while (*p++ != '\0')
|
|
continue;
|
|
while (*p == A_SKIP)
|
|
p++;
|
|
if (*p & A_EXTRA)
|
|
while (*++p != '\0')
|
|
continue;
|
|
q = cmd-1;
|
|
}
|
|
}
|
|
/*
|
|
* No match found in the entire command table.
|
|
*/
|
|
return (A_INVALID);
|
|
}
|
|
|
|
/*
|
|
* Decode a command character and return the associated action.
|
|
* The "extra" string, if any, is returned in sp.
|
|
*/
|
|
static int
|
|
cmd_decode(struct tablelist *tlist, char *cmd, char **sp)
|
|
{
|
|
struct tablelist *t;
|
|
int action = A_INVALID;
|
|
|
|
/*
|
|
* Search thru all the command tables.
|
|
* Stop when we find an action which is not A_INVALID.
|
|
*/
|
|
for (t = tlist; t != NULL; t = t->t_next)
|
|
{
|
|
action = cmd_search(cmd, t->t_start, t->t_end, sp);
|
|
if (action != A_INVALID)
|
|
break;
|
|
}
|
|
if (action == A_UINVALID)
|
|
action = A_INVALID;
|
|
return (action);
|
|
}
|
|
|
|
/*
|
|
* Decode a command from the cmdtables list.
|
|
*/
|
|
public int
|
|
fcmd_decode(char *cmd, char **sp)
|
|
{
|
|
return (cmd_decode(list_fcmd_tables, cmd, sp));
|
|
}
|
|
|
|
/*
|
|
* Decode a command from the edittables list.
|
|
*/
|
|
public int
|
|
ecmd_decode(char *cmd, char **sp)
|
|
{
|
|
return (cmd_decode(list_ecmd_tables, cmd, sp));
|
|
}
|
|
|
|
/*
|
|
* Get the value of an environment variable.
|
|
* Looks first in the lesskey file, then in the real environment.
|
|
*/
|
|
public char *
|
|
lgetenv(char *var)
|
|
{
|
|
int a;
|
|
char *s;
|
|
|
|
a = cmd_decode(list_var_tables, var, &s);
|
|
if (a == EV_OK)
|
|
return (s);
|
|
s = getenv(var);
|
|
if (s != NULL && *s != '\0')
|
|
return (s);
|
|
a = cmd_decode(list_sysvar_tables, var, &s);
|
|
if (a == EV_OK)
|
|
return (s);
|
|
return (NULL);
|
|
}
|
|
|
|
#if USERFILE
|
|
/*
|
|
* Get an "integer" from a lesskey file.
|
|
* Integers are stored in a funny format:
|
|
* two bytes, low order first, in radix KRADIX.
|
|
*/
|
|
static int
|
|
gint(char **sp)
|
|
{
|
|
int n;
|
|
|
|
n = *(*sp)++;
|
|
n += *(*sp)++ * KRADIX;
|
|
return (n);
|
|
}
|
|
|
|
/*
|
|
* Process an old (pre-v241) lesskey file.
|
|
*/
|
|
static int
|
|
old_lesskey(char *buf, int len)
|
|
{
|
|
/*
|
|
* Old-style lesskey file.
|
|
* The file must end with either
|
|
* ...,cmd,0,action
|
|
* or ...,cmd,0,action|A_EXTRA,string,0
|
|
* So the last byte or the second to last byte must be zero.
|
|
*/
|
|
if (buf[len-1] != '\0' && buf[len-2] != '\0')
|
|
return (-1);
|
|
add_fcmd_table(buf, len);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Process a new (post-v241) lesskey file.
|
|
*/
|
|
static int
|
|
new_lesskey(char *buf, int len, int sysvar)
|
|
{
|
|
char *p;
|
|
int c;
|
|
int n;
|
|
|
|
/*
|
|
* New-style lesskey file.
|
|
* Extract the pieces.
|
|
*/
|
|
if (buf[len-3] != C0_END_LESSKEY_MAGIC ||
|
|
buf[len-2] != C1_END_LESSKEY_MAGIC ||
|
|
buf[len-1] != C2_END_LESSKEY_MAGIC)
|
|
return (-1);
|
|
p = buf + 4;
|
|
for (;;)
|
|
{
|
|
c = *p++;
|
|
switch (c)
|
|
{
|
|
case CMD_SECTION:
|
|
n = gint(&p);
|
|
add_fcmd_table(p, n);
|
|
p += n;
|
|
break;
|
|
case EDIT_SECTION:
|
|
n = gint(&p);
|
|
add_ecmd_table(p, n);
|
|
p += n;
|
|
break;
|
|
case VAR_SECTION:
|
|
n = gint(&p);
|
|
add_var_table((sysvar) ?
|
|
&list_sysvar_tables : &list_var_tables, p, n);
|
|
p += n;
|
|
break;
|
|
case END_SECTION:
|
|
return (0);
|
|
default:
|
|
/*
|
|
* Unrecognized section type.
|
|
*/
|
|
return (-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set up a user command table, based on a "lesskey" file.
|
|
*/
|
|
public int
|
|
lesskey(char *filename, int sysvar)
|
|
{
|
|
char *buf;
|
|
POSITION len;
|
|
long n;
|
|
int f;
|
|
|
|
if (secure)
|
|
return (1);
|
|
/*
|
|
* Try to open the lesskey file.
|
|
*/
|
|
filename = shell_unquote(filename);
|
|
f = open(filename, OPEN_READ);
|
|
free(filename);
|
|
if (f < 0)
|
|
return (1);
|
|
|
|
/*
|
|
* Read the file into a buffer.
|
|
* We first figure out the size of the file and allocate space for it.
|
|
* {{ Minimal error checking is done here.
|
|
* A garbage .less file will produce strange results.
|
|
* To avoid a large amount of error checking code here, we
|
|
* rely on the lesskey program to generate a good .less file. }}
|
|
*/
|
|
len = filesize(f);
|
|
if (len == NULL_POSITION || len < 3)
|
|
{
|
|
/*
|
|
* Bad file (valid file must have at least 3 chars).
|
|
*/
|
|
close(f);
|
|
return (-1);
|
|
}
|
|
if ((buf = (char *) calloc((int)len, sizeof(char))) == NULL)
|
|
{
|
|
close(f);
|
|
return (-1);
|
|
}
|
|
if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK)
|
|
{
|
|
free(buf);
|
|
close(f);
|
|
return (-1);
|
|
}
|
|
n = read(f, buf, (unsigned int) len);
|
|
close(f);
|
|
if (n != len)
|
|
{
|
|
free(buf);
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Figure out if this is an old-style (before version 241)
|
|
* or new-style lesskey file format.
|
|
*/
|
|
if (buf[0] != C0_LESSKEY_MAGIC || buf[1] != C1_LESSKEY_MAGIC ||
|
|
buf[2] != C2_LESSKEY_MAGIC || buf[3] != C3_LESSKEY_MAGIC)
|
|
return (old_lesskey(buf, (int)len));
|
|
return (new_lesskey(buf, (int)len, sysvar));
|
|
}
|
|
|
|
/*
|
|
* Add the standard lesskey file "$HOME/.less"
|
|
*/
|
|
public void
|
|
add_hometable(char *envname, char *def_filename, int sysvar)
|
|
{
|
|
char *filename;
|
|
PARG parg;
|
|
|
|
if (envname != NULL && (filename = lgetenv(envname)) != NULL)
|
|
filename = save(filename);
|
|
else if (sysvar)
|
|
filename = save(def_filename);
|
|
else
|
|
filename = homefile(def_filename);
|
|
if (filename == NULL)
|
|
return;
|
|
if (lesskey(filename, sysvar) < 0)
|
|
{
|
|
parg.p_string = filename;
|
|
error("Cannot use lesskey file \"%s\"", &parg);
|
|
}
|
|
free(filename);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* See if a char is a special line-editing command.
|
|
*/
|
|
public int
|
|
editchar(int c, int flags)
|
|
{
|
|
int action;
|
|
int nch;
|
|
char *s;
|
|
char usercmd[MAX_CMDLEN+1];
|
|
|
|
/*
|
|
* An editing character could actually be a sequence of characters;
|
|
* for example, an escape sequence sent by pressing the uparrow key.
|
|
* To match the editing string, we use the command decoder
|
|
* but give it the edit-commands command table
|
|
* This table is constructed to match the user's keyboard.
|
|
*/
|
|
if (c == erase_char || c == erase2_char)
|
|
return (EC_BACKSPACE);
|
|
if (c == kill_char)
|
|
return (EC_LINEKILL);
|
|
|
|
/*
|
|
* Collect characters in a buffer.
|
|
* Start with the one we have, and get more if we need them.
|
|
*/
|
|
nch = 0;
|
|
do {
|
|
if (nch > 0)
|
|
c = getcc();
|
|
usercmd[nch] = c;
|
|
usercmd[nch+1] = '\0';
|
|
nch++;
|
|
action = ecmd_decode(usercmd, &s);
|
|
} while (action == A_PREFIX);
|
|
|
|
if (flags & EC_NORIGHTLEFT)
|
|
{
|
|
switch (action)
|
|
{
|
|
case EC_RIGHT:
|
|
case EC_LEFT:
|
|
action = A_INVALID;
|
|
break;
|
|
}
|
|
}
|
|
#if CMD_HISTORY
|
|
if (flags & EC_NOHISTORY)
|
|
{
|
|
/*
|
|
* The caller says there is no history list.
|
|
* Reject any history-manipulation action.
|
|
*/
|
|
switch (action)
|
|
{
|
|
case EC_UP:
|
|
case EC_DOWN:
|
|
action = A_INVALID;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
#if TAB_COMPLETE_FILENAME
|
|
if (flags & EC_NOCOMPLETE)
|
|
{
|
|
/*
|
|
* The caller says we don't want any filename completion cmds.
|
|
* Reject them.
|
|
*/
|
|
switch (action)
|
|
{
|
|
case EC_F_COMPLETE:
|
|
case EC_B_COMPLETE:
|
|
case EC_EXPAND:
|
|
action = A_INVALID;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
if ((flags & EC_PEEK) || action == A_INVALID)
|
|
{
|
|
/*
|
|
* We're just peeking, or we didn't understand the command.
|
|
* Unget all the characters we read in the loop above.
|
|
* This does NOT include the original character that was
|
|
* passed in as a parameter.
|
|
*/
|
|
while (nch > 1)
|
|
{
|
|
ungetcc(usercmd[--nch]);
|
|
}
|
|
} else
|
|
{
|
|
if (s != NULL)
|
|
ungetsc(s);
|
|
}
|
|
return action;
|
|
}
|
|
|