gno/bin/less/decode.c
gdr-ftp 784e3de7cd Initial checkin of aroff, binprint, center, less, ls, make, makemake,
passwd, ps, purge, shutdown, stty, upper, and vi.  These sources are
for the versions of the utils shipped with GNO v2.0.4.
1998-03-09 08:30:21 +00:00

374 lines
8.2 KiB
C

/*
* 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.
*/
#pragma noroot
#include "less.h"
#include "cmd.h"
#if __MSDOS__
#include <io.h>
#include <stdlib.h>
#endif
#ifdef _ORCAC_
#include <fcntl.h>
segment "LoadSegONE";
#endif
/*
* Command table is ordered roughly according to expected
* frequency of use, so the common commands are near the beginning.
*/
static char cmdtable[] =
{
#if __MSDOS__
/*
* PC function keys.
* Note that '\0' is converted to '\200' on input.
*/
'\200','\120',0, A_F_LINE, /* down arrow */
'\200','\121',0, A_F_SCREEN, /* page down */
'\200','\110',0, A_B_LINE, /* up arrow */
'\200','\111',0, A_B_SCREEN, /* page up */
'\200','\107',0, A_GOLINE, /* home */
'\200','\117',0, A_GOEND, /* end */
'\200','\073',0, A_HELP, /* F1 */
'\200','\104',0, A_MODIFY_WINDOW, /* F10 */
'\200','\103',0, A_MODIFY_COLOURS, /* F9 */
#endif
'\r',0, A_F_LINE,
'\n',0, A_F_LINE,
'e',0, A_F_LINE,
'j',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,
CONTROL('K'),0, A_B_LINE,
CONTROL('P'),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,
'b',0, A_B_SCREEN,
CONTROL('B'),0, A_B_SCREEN,
ESC,'v',0, A_B_SCREEN,
'z',0, A_F_WINDOW,
'w',0, A_B_WINDOW,
'F',0, A_F_FOREVER,
'R',0, A_FREPAINT,
'r',0, A_REPAINT,
CONTROL('R'),0, A_REPAINT,
CONTROL('L'),0, A_REPAINT,
'g',0, A_GOLINE,
'<',0, A_GOLINE,
ESC,'<',0, A_GOLINE,
'p',0, A_PERCENT,
'%',0, A_PERCENT,
'{',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,'>',0, A_GOEND,
'>',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_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,
'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,
':','x',0, A_INDEX_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,
'V',0, A_VERSION,
'q',0, A_QUIT,
':','q',0, A_QUIT,
':','Q',0, A_QUIT,
'Z','Z',0, A_QUIT,
ESC,ESC,0, A_QUIT,
};
/*
* Structure to support a list of command tables.
*/
struct tablelist
{
struct tablelist *t_next;
char *t_start;
char *t_end;
};
/*
* Structure for the default command table.
*/
static struct tablelist deftable =
{ NULL, cmdtable, cmdtable+sizeof(cmdtable) };
/*
* List of tables; initially contains only the default table.
*/
static struct tablelist *tables = &deftable;
static int cmd_search(char *cmd, char *table, char *endtable, char **sp);
extern int erase_char, kill_char;
/*
* Decode a command character and return the associated action.
* The "extra" string, if any, is returned in sp.
*/
public int
cmd_decode(cmd, sp)
char *cmd;
char **sp;
{
register struct tablelist *t;
register int action;
/*
* Search thru all the command tables.
* Stop when we find an action which is not A_INVALID.
*/
for (t = tables; t != NULL; t = t->t_next)
{
action = cmd_search(cmd, t->t_start, t->t_end, sp);
if (action != A_INVALID)
break;
}
return (action);
}
/*
* Search a command table for the current command string (in cmd).
*/
static int
cmd_search(cmd, table, endtable, sp)
char *cmd;
char *table;
char *endtable;
char **sp;
{
register char *p;
register char *q;
register int a;
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;
/*
* Check for an "extra" string.
*/
if (a & A_EXTRA)
{
*sp = ++p;
a &= ~A_EXTRA;
} else
*sp = NULL;
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.
*/
while (*p++ != '\0') ;
if (*p & A_EXTRA)
while (*++p != '\0') ;
q = cmd-1;
}
}
/*
* No match found in the entire command table.
*/
return (A_INVALID);
}
#if USERFILE
/*
* Set up a user command table, based on a "lesskey" file.
*/
public int
add_cmdtable(filename)
char *filename;
{
register struct tablelist *t;
register POSITION len;
register long n;
register int f;
/*
* Try to open the lesskey file.
* If we can't, return an error.
*/
f = open(filename, O_RDONLY);
if (f < 0)
return (-1);
/*
* Read the file into the user table.
* 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 ((t = (struct tablelist *)
calloc(1, sizeof(struct tablelist))) == NULL)
{
close(f);
return (-1);
}
if ((t->t_start = (char *) calloc(len, sizeof(char))) == NULL)
{
free((char *)t);
close(f);
return (-1);
}
if (lseek(f, (offset_t)0, 0) == BAD_LSEEK)
{
free(t->t_start);
free((char *)t);
close(f);
return (-1);
}
n = read(f, t->t_start, (size_t) len);
close(f);
/*
* In a valid lesskey file, the last byte or
* the second to the last byte must be zero.
*/
if (n != len || (t->t_start[n-1] != '\0' && t->t_start[n-2] != '\0'))
{
free(t->t_start);
free((char *)t);
return (-1);
}
t->t_end = t->t_start + n;
/*
* Link it into the list of tables.
*/
t->t_next = tables;
tables = t;
return (0);
}
/*
* Try to add the lesskey file "$HOME/.less"
*/
public void
add_hometable(void)
{
char *filename;
#if __MSDOS__
filename = homefile("_less");
#else
filename = homefile("lessrc");
#endif
if (filename == NULL)
return;
/*
* Ignore errors.
*/
(void) add_cmdtable(filename);
free(filename);
}
#endif