commands: less is more.
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
# $NetBSD: Makefile,v 1.5 1997/10/24 09:00:39 lukem Exp $
|
||||
|
||||
PROG= lesskey
|
||||
SRCS= lesskey.c version.c
|
||||
CPPFLAGS+=-I${.CURDIR}/../less
|
||||
|
||||
.PATH: ${.CURDIR}/../less
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
@@ -0,0 +1,386 @@
|
||||
.\" $NetBSD: lesskey.1,v 1.9 2006/10/26 01:33:08 mrg Exp $
|
||||
.\"
|
||||
.TH LESSKEY 1 "Version 394: 03 Dec 2005"
|
||||
.SH NAME
|
||||
lesskey \- specify key bindings for less
|
||||
.SH SYNOPSIS
|
||||
.B "lesskey [-o output] [--] [input]"
|
||||
.br
|
||||
.B "lesskey [--output=output] [--] [input]"
|
||||
.br
|
||||
.B "lesskey -V"
|
||||
.br
|
||||
.B "lesskey --version"
|
||||
.SH DESCRIPTION
|
||||
.I Lesskey
|
||||
is used to specify a set of key bindings to be used by
|
||||
.I less.
|
||||
The input file is a text file which describes the key bindings,
|
||||
If the input file is "-", standard input is read.
|
||||
If no input file is specified, a standard filename is used
|
||||
as the name of the input file, which depends on the system being used:
|
||||
On Unix systems, $HOME/.lesskey is used;
|
||||
on MS-DOS systems, $HOME/_lesskey is used;
|
||||
and on OS/2 systems $HOME/lesskey.ini is used,
|
||||
or $INIT/lesskey.ini if $HOME is undefined.
|
||||
The output file is a binary file which is used by
|
||||
.I less.
|
||||
If no output file is specified,
|
||||
and the environment variable LESSKEY is set,
|
||||
the value of LESSKEY is used as the name of the output file.
|
||||
Otherwise, a standard filename is used as the name of the output file,
|
||||
which depends on the system being used:
|
||||
On Unix and OS-9 systems, $HOME/.less is used;
|
||||
on MS-DOS systems, $HOME/_less is used;
|
||||
and on OS/2 systems, $HOME/less.ini is used,
|
||||
or $INIT/less.ini if $HOME is undefined.
|
||||
If the output file already exists,
|
||||
.I lesskey
|
||||
will overwrite it.
|
||||
.PP
|
||||
The -V or --version option causes
|
||||
.I lesskey
|
||||
to print its version number and immediately exit.
|
||||
If -V or --version is present, other options and arguments are ignored.
|
||||
.PP
|
||||
The input file consists of one or more
|
||||
.I sections.
|
||||
Each section starts with a line that identifies the type of section.
|
||||
Possible sections are:
|
||||
.IP #command
|
||||
Defines new command keys.
|
||||
.IP #line-edit
|
||||
Defines new line-editing keys.
|
||||
.IP #env
|
||||
Defines environment variables.
|
||||
.PP
|
||||
Blank lines and lines which start with a pound sign (#) are ignored,
|
||||
except for the special section header lines.
|
||||
|
||||
.SH "COMMAND SECTION"
|
||||
The command section begins with the line
|
||||
.sp
|
||||
#command
|
||||
.sp
|
||||
If the command section is the first section in the file,
|
||||
this line may be omitted.
|
||||
The command section consists of lines of the form:
|
||||
.sp
|
||||
\fIstring\fP \*[Lt]whitespace\*[Gt] \fIaction\fP [extra-string] \*[Lt]newline\*[Gt]
|
||||
.sp
|
||||
Whitespace is any sequence of one or more spaces and/or tabs.
|
||||
The \fIstring\fP is the command key(s) which invoke the action.
|
||||
The \fIstring\fP may be a single command key, or a sequence of up to 15 keys.
|
||||
The \fIaction\fP is the name of the less action, from the list below.
|
||||
The characters in the \fIstring\fP may appear literally, or be
|
||||
prefixed by a caret to indicate a control key.
|
||||
A backslash followed by one to three octal digits may be used to
|
||||
specify a character by its octal value.
|
||||
A backslash followed by certain characters specifies input
|
||||
characters as follows:
|
||||
.IP \eb
|
||||
BACKSPACE
|
||||
.IP \ee
|
||||
ESCAPE
|
||||
.IP \en
|
||||
NEWLINE
|
||||
.IP \er
|
||||
RETURN
|
||||
.IP \et
|
||||
TAB
|
||||
.IP \eku
|
||||
UP ARROW
|
||||
.IP \ekd
|
||||
DOWN ARROW
|
||||
.IP \ekr
|
||||
RIGHT ARROW
|
||||
.IP \ekl
|
||||
LEFT ARROW
|
||||
.IP \ekU
|
||||
PAGE UP
|
||||
.IP \ekD
|
||||
PAGE DOWN
|
||||
.IP \ekh
|
||||
HOME
|
||||
.IP \eke
|
||||
END
|
||||
.IP \ekx
|
||||
DELETE
|
||||
.PP
|
||||
A backslash followed by any other character indicates that character is
|
||||
to be taken literally.
|
||||
Characters which must be preceded by backslash include
|
||||
caret, space, tab and the backslash itself.
|
||||
.PP
|
||||
An action may be followed by an "extra" string.
|
||||
When such a command is entered while running
|
||||
.I less,
|
||||
the action is performed, and then the extra
|
||||
string is parsed, just as if it were typed in to
|
||||
.I less.
|
||||
This feature can be used in certain cases to extend
|
||||
the functionality of a command.
|
||||
For example, see the "{" and ":t" commands in the example below.
|
||||
The extra string has a special meaning for the "quit" action:
|
||||
when
|
||||
.I less
|
||||
quits, first character of the extra string is used as its exit status.
|
||||
|
||||
.SH EXAMPLE
|
||||
The following input file describes the set of
|
||||
default command keys used by less:
|
||||
.sp
|
||||
.nf
|
||||
#command
|
||||
\er forw-line
|
||||
\en forw-line
|
||||
e forw-line
|
||||
j forw-line
|
||||
\ekd forw-line
|
||||
^E forw-line
|
||||
^N forw-line
|
||||
k back-line
|
||||
y back-line
|
||||
^Y back-line
|
||||
^K back-line
|
||||
^P back-line
|
||||
J forw-line-force
|
||||
K back-line-force
|
||||
Y back-line-force
|
||||
d forw-scroll
|
||||
^D forw-scroll
|
||||
u back-scroll
|
||||
^U back-scroll
|
||||
\e40 forw-screen
|
||||
f forw-screen
|
||||
^F forw-screen
|
||||
^V forw-screen
|
||||
\ekD forw-screen
|
||||
b back-screen
|
||||
^B back-screen
|
||||
\eev back-screen
|
||||
\ekU back-screen
|
||||
z forw-window
|
||||
w back-window
|
||||
\ee\e40 forw-screen-force
|
||||
F forw-forever
|
||||
R repaint-flush
|
||||
r repaint
|
||||
^R repaint
|
||||
^L repaint
|
||||
\eeu undo-hilite
|
||||
g goto-line
|
||||
\ekh goto-line
|
||||
\*[Lt] goto-line
|
||||
\ee\*[Lt] goto-line
|
||||
p percent
|
||||
% percent
|
||||
\ee[ left-scroll
|
||||
\ee] right-scroll
|
||||
\ee( left-scroll
|
||||
\ee) right-scroll
|
||||
{ forw-bracket {}
|
||||
} back-bracket {}
|
||||
( forw-bracket ()
|
||||
) back-bracket ()
|
||||
[ forw-bracket []
|
||||
] back-bracket []
|
||||
\ee^F forw-bracket
|
||||
\ee^B back-bracket
|
||||
G goto-end
|
||||
\ee\*[Gt] goto-end
|
||||
\*[Gt] goto-end
|
||||
\eke goto-end
|
||||
= status
|
||||
^G status
|
||||
:f status
|
||||
/ forw-search
|
||||
? back-search
|
||||
\ee/ forw-search *
|
||||
\ee? back-search *
|
||||
n repeat-search
|
||||
\een repeat-search-all
|
||||
N reverse-search
|
||||
\eeN reverse-search-all
|
||||
m set-mark
|
||||
' goto-mark
|
||||
^X^X goto-mark
|
||||
E examine
|
||||
:e examine
|
||||
^X^V examine
|
||||
:n next-file
|
||||
:p prev-file
|
||||
t next-tag
|
||||
T prev-tag
|
||||
:x index-file
|
||||
:d remove-file
|
||||
- toggle-option
|
||||
:t toggle-option t
|
||||
s toggle-option o
|
||||
_ display-option
|
||||
| pipe
|
||||
v visual
|
||||
! shell
|
||||
+ firstcmd
|
||||
H help
|
||||
h help
|
||||
V version
|
||||
0 digit
|
||||
1 digit
|
||||
2 digit
|
||||
3 digit
|
||||
4 digit
|
||||
5 digit
|
||||
6 digit
|
||||
7 digit
|
||||
8 digit
|
||||
9 digit
|
||||
q quit
|
||||
Q quit
|
||||
:q quit
|
||||
:Q quit
|
||||
ZZ quit
|
||||
.fi
|
||||
.sp
|
||||
.SH PRECEDENCE
|
||||
Commands specified by
|
||||
.I lesskey
|
||||
take precedence over the default commands.
|
||||
A default command key may be disabled by including it in the
|
||||
input file with the action "invalid".
|
||||
Alternatively, a key may be defined
|
||||
to do nothing by using the action "noaction".
|
||||
"noaction" is similar to "invalid", but
|
||||
.I less
|
||||
will give an error beep for an "invalid" command,
|
||||
but not for a "noaction" command.
|
||||
In addition, ALL default commands may be disabled by
|
||||
adding this control line to the input file:
|
||||
.sp
|
||||
#stop
|
||||
.sp
|
||||
This will cause all default commands to be ignored.
|
||||
The #stop line should be the last line in that section of the file.
|
||||
.PP
|
||||
Be aware that #stop can be dangerous.
|
||||
Since all default commands are disabled,
|
||||
you must provide sufficient commands before the #stop line
|
||||
to enable all necessary actions.
|
||||
For example, failure to provide a "quit" command can lead to frustration.
|
||||
|
||||
.SH "LINE EDITING SECTION"
|
||||
The line-editing section begins with the line:
|
||||
.sp
|
||||
#line-edit
|
||||
.sp
|
||||
This section specifies new key bindings for the line editing commands,
|
||||
in a manner similar to the way key bindings for
|
||||
ordinary commands are specified in the #command section.
|
||||
The line-editing section consists of a list of keys and actions,
|
||||
one per line as in the example below.
|
||||
|
||||
.SH EXAMPLE
|
||||
The following input file describes the set of
|
||||
default line-editing keys used by less:
|
||||
.sp
|
||||
.nf
|
||||
#line-edit
|
||||
\et forw-complete
|
||||
\e17 back-complete
|
||||
\ee\et back-complete
|
||||
^L expand
|
||||
^V literal
|
||||
^A literal
|
||||
\eel right
|
||||
\ekr right
|
||||
\eeh left
|
||||
\ekl left
|
||||
\eeb word-left
|
||||
\ee\ekl word-left
|
||||
\eew word-right
|
||||
\ee\ekr word-right
|
||||
\eei insert
|
||||
\eex delete
|
||||
\ekx delete
|
||||
\eeX word-delete
|
||||
\eekx word-delete
|
||||
\ee\eb word-backspace
|
||||
\ee0 home
|
||||
\ekh home
|
||||
\ee$ end
|
||||
\eke end
|
||||
\eek up
|
||||
\eku up
|
||||
\eej down
|
||||
.fi
|
||||
.sp
|
||||
|
||||
.SH "LESS ENVIRONMENT VARIABLES"
|
||||
The environment variable section begins with the line
|
||||
.sp
|
||||
#env
|
||||
.sp
|
||||
Following this line is a list of environment variable assignments.
|
||||
Each line consists of an environment variable name, an equals sign (=)
|
||||
and the value to be assigned to the environment variable.
|
||||
White space before and after the equals sign is ignored.
|
||||
Variables assigned in this way are visible only to
|
||||
.I less.
|
||||
If a variable is specified in the system environment and also in a
|
||||
lesskey file, the value in the lesskey file takes precedence.
|
||||
Although the lesskey file can be used to override variables set in the
|
||||
environment, the main purpose of assigning variables in the lesskey file
|
||||
is simply to have all
|
||||
.I less
|
||||
configuration information stored in one file.
|
||||
|
||||
.SH EXAMPLE
|
||||
The following input file sets the -i option whenever
|
||||
.I less
|
||||
is run, and specifies the character set to be "latin1":
|
||||
.sp
|
||||
.nf
|
||||
#env
|
||||
LESS = -i
|
||||
LESSCHARSET = latin1
|
||||
.fi
|
||||
.sp
|
||||
|
||||
.SH "SEE ALSO"
|
||||
less(1)
|
||||
|
||||
.SH WARNINGS
|
||||
It is not possible to specify special keys, such as uparrow,
|
||||
in a keyboard-independent manner.
|
||||
The only way to specify such keys is to specify the escape sequence
|
||||
which a particular keyboard sends when such a key is pressed.
|
||||
.PP
|
||||
On MS-DOS and OS/2 systems, certain keys send a sequence of characters
|
||||
which start with a NUL character (0).
|
||||
This NUL character should be represented as \e340 in a lesskey file.
|
||||
|
||||
.SH COPYRIGHT
|
||||
Copyright (C) 2004 Mark Nudelman
|
||||
.PP
|
||||
lesskey is part of the GNU project and is free software;
|
||||
you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation;
|
||||
either version 2, or (at your option) any later version.
|
||||
.PP
|
||||
lesskey is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU General Public License for more details.
|
||||
.PP
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with lesskey; see the file COPYING.
|
||||
If not, write to the Free Software Foundation, 59 Temple Place,
|
||||
Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
.SH AUTHOR
|
||||
.PP
|
||||
Mark Nudelman \*[Lt]markn@greenwoodsoftware.com\*[Gt]
|
||||
.br
|
||||
Send bug reports or comments to the above address or to bug-less@gnu.org.
|
||||
|
||||
@@ -0,0 +1,875 @@
|
||||
/* $NetBSD: lesskey.c,v 1.9 2006/10/26 01:33:08 mrg Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (C) 1984-2004 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 about less, or for information on how to
|
||||
* contact the author, see the README file.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* lesskey [-o output] [input]
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Make a .less file.
|
||||
* If no input file is specified, standard input is used.
|
||||
* If no output file is specified, $HOME/.less is used.
|
||||
*
|
||||
* The .less file is used to specify (to "less") user-defined
|
||||
* key bindings. Basically any sequence of 1 to MAX_CMDLEN
|
||||
* keystrokes may be bound to an existing less function.
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* The input file is an ascii file consisting of a
|
||||
* sequence of lines of the form:
|
||||
* string <whitespace> action [chars] <newline>
|
||||
*
|
||||
* "string" is a sequence of command characters which form
|
||||
* the new user-defined command. The command
|
||||
* characters may be:
|
||||
* 1. The actual character itself.
|
||||
* 2. A character preceded by ^ to specify a
|
||||
* control character (e.g. ^X means control-X).
|
||||
* 3. A backslash followed by one to three octal digits
|
||||
* to specify a character by its octal value.
|
||||
* 4. A backslash followed by b, e, n, r or t
|
||||
* to specify \b, ESC, \n, \r or \t, respectively.
|
||||
* 5. Any character (other than those mentioned above) preceded
|
||||
* by a \ to specify the character itself (characters which
|
||||
* must be preceded by \ include ^, \, and whitespace.
|
||||
* "action" is the name of a "less" action, from the table below.
|
||||
* "chars" is an optional sequence of characters which is treated
|
||||
* as keyboard input after the command is executed.
|
||||
*
|
||||
* Blank lines and lines which start with # are ignored,
|
||||
* except for the special control lines:
|
||||
* #command Signals the beginning of the command
|
||||
* keys section.
|
||||
* #line-edit Signals the beginning of the line-editing
|
||||
* keys section.
|
||||
* #env Signals the beginning of the environment
|
||||
* variable section.
|
||||
* #stop Stops command parsing in less;
|
||||
* causes all default keys to be disabled.
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* The output file is a non-ascii file, consisting of a header,
|
||||
* one or more sections, and a trailer.
|
||||
* Each section begins with a section header, a section length word
|
||||
* and the section data. Normally there are three sections:
|
||||
* CMD_SECTION Definition of command keys.
|
||||
* EDIT_SECTION Definition of editing keys.
|
||||
* END_SECTION A special section header, with no
|
||||
* length word or section data.
|
||||
*
|
||||
* Section data consists of zero or more byte sequences of the form:
|
||||
* string <0> <action>
|
||||
* or
|
||||
* string <0> <action|A_EXTRA> chars <0>
|
||||
*
|
||||
* "string" is the command string.
|
||||
* "<0>" is one null byte.
|
||||
* "<action>" is one byte containing the action code (the A_xxx value).
|
||||
* If action is ORed with A_EXTRA, the action byte is followed
|
||||
* by the null-terminated "chars" string.
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*/
|
||||
|
||||
#include "less.h"
|
||||
#include "lesskey.h"
|
||||
#include "cmd.h"
|
||||
|
||||
struct cmdname
|
||||
{
|
||||
char *cn_name;
|
||||
int cn_action;
|
||||
};
|
||||
|
||||
struct cmdname cmdnames[] =
|
||||
{
|
||||
{ "back-bracket", A_B_BRACKET },
|
||||
{ "back-line", A_B_LINE },
|
||||
{ "back-line-force", A_BF_LINE },
|
||||
{ "back-screen", A_B_SCREEN },
|
||||
{ "back-scroll", A_B_SCROLL },
|
||||
{ "back-search", A_B_SEARCH },
|
||||
{ "back-window", A_B_WINDOW },
|
||||
{ "debug", A_DEBUG },
|
||||
{ "digit", A_DIGIT },
|
||||
{ "display-flag", A_DISP_OPTION },
|
||||
{ "display-option", A_DISP_OPTION },
|
||||
{ "end", A_GOEND },
|
||||
{ "examine", A_EXAMINE },
|
||||
{ "first-cmd", A_FIRSTCMD },
|
||||
{ "firstcmd", A_FIRSTCMD },
|
||||
{ "flush-repaint", A_FREPAINT },
|
||||
{ "forw-bracket", A_F_BRACKET },
|
||||
{ "forw-forever", A_F_FOREVER },
|
||||
{ "forw-line", A_F_LINE },
|
||||
{ "forw-line-force", A_FF_LINE },
|
||||
{ "forw-screen", A_F_SCREEN },
|
||||
{ "forw-screen-force", A_FF_SCREEN },
|
||||
{ "forw-scroll", A_F_SCROLL },
|
||||
{ "forw-search", A_F_SEARCH },
|
||||
{ "forw-window", A_F_WINDOW },
|
||||
{ "goto-end", A_GOEND },
|
||||
{ "goto-line", A_GOLINE },
|
||||
{ "goto-mark", A_GOMARK },
|
||||
{ "help", A_HELP },
|
||||
{ "index-file", A_INDEX_FILE },
|
||||
{ "invalid", A_UINVALID },
|
||||
{ "left-scroll", A_LSHIFT },
|
||||
{ "next-file", A_NEXT_FILE },
|
||||
{ "next-tag", A_NEXT_TAG },
|
||||
{ "noaction", A_NOACTION },
|
||||
{ "percent", A_PERCENT },
|
||||
{ "pipe", A_PIPE },
|
||||
{ "prev-file", A_PREV_FILE },
|
||||
{ "prev-tag", A_PREV_TAG },
|
||||
{ "quit", A_QUIT },
|
||||
{ "remove-file", A_REMOVE_FILE },
|
||||
{ "repaint", A_REPAINT },
|
||||
{ "repaint-flush", A_FREPAINT },
|
||||
{ "repeat-search", A_AGAIN_SEARCH },
|
||||
{ "repeat-search-all", A_T_AGAIN_SEARCH },
|
||||
{ "reverse-search", A_REVERSE_SEARCH },
|
||||
{ "reverse-search-all", A_T_REVERSE_SEARCH },
|
||||
{ "right-scroll", A_RSHIFT },
|
||||
{ "set-mark", A_SETMARK },
|
||||
{ "shell", A_SHELL },
|
||||
{ "status", A_STAT },
|
||||
{ "toggle-flag", A_OPT_TOGGLE },
|
||||
{ "toggle-option", A_OPT_TOGGLE },
|
||||
{ "undo-hilite", A_UNDO_SEARCH },
|
||||
{ "version", A_VERSION },
|
||||
{ "visual", A_VISUAL },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
struct cmdname editnames[] =
|
||||
{
|
||||
{ "back-complete", EC_B_COMPLETE },
|
||||
{ "backspace", EC_BACKSPACE },
|
||||
{ "delete", EC_DELETE },
|
||||
{ "down", EC_DOWN },
|
||||
{ "end", EC_END },
|
||||
{ "expand", EC_EXPAND },
|
||||
{ "forw-complete", EC_F_COMPLETE },
|
||||
{ "home", EC_HOME },
|
||||
{ "insert", EC_INSERT },
|
||||
{ "invalid", EC_UINVALID },
|
||||
{ "kill-line", EC_LINEKILL },
|
||||
{ "left", EC_LEFT },
|
||||
{ "literal", EC_LITERAL },
|
||||
{ "right", EC_RIGHT },
|
||||
{ "up", EC_UP },
|
||||
{ "word-backspace", EC_W_BACKSPACE },
|
||||
{ "word-delete", EC_W_DELETE },
|
||||
{ "word-left", EC_W_LEFT },
|
||||
{ "word-right", EC_W_RIGHT },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
struct table
|
||||
{
|
||||
struct cmdname *names;
|
||||
char *pbuffer;
|
||||
char buffer[MAX_USERCMD];
|
||||
};
|
||||
|
||||
struct table cmdtable;
|
||||
struct table edittable;
|
||||
struct table vartable;
|
||||
struct table *currtable = &cmdtable;
|
||||
|
||||
char fileheader[] = {
|
||||
C0_LESSKEY_MAGIC,
|
||||
C1_LESSKEY_MAGIC,
|
||||
C2_LESSKEY_MAGIC,
|
||||
C3_LESSKEY_MAGIC
|
||||
};
|
||||
char filetrailer[] = {
|
||||
C0_END_LESSKEY_MAGIC,
|
||||
C1_END_LESSKEY_MAGIC,
|
||||
C2_END_LESSKEY_MAGIC
|
||||
};
|
||||
char cmdsection[1] = { CMD_SECTION };
|
||||
char editsection[1] = { EDIT_SECTION };
|
||||
char varsection[1] = { VAR_SECTION };
|
||||
char endsection[1] = { END_SECTION };
|
||||
|
||||
char *infile = NULL;
|
||||
char *outfile = NULL ;
|
||||
|
||||
int linenum;
|
||||
int errors;
|
||||
|
||||
void terror(char *);
|
||||
|
||||
extern char version[];
|
||||
|
||||
void
|
||||
usage()
|
||||
{
|
||||
fprintf(stderr, "usage: lesskey [-o output] [input]\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char *
|
||||
mkpathname(dirname, filename)
|
||||
char *dirname;
|
||||
char *filename;
|
||||
{
|
||||
char *pathname;
|
||||
|
||||
pathname = calloc(strlen(dirname) + strlen(filename) + 2, sizeof(char));
|
||||
strcpy(pathname, dirname);
|
||||
strcat(pathname, PATHNAME_SEP);
|
||||
strcat(pathname, filename);
|
||||
return (pathname);
|
||||
}
|
||||
|
||||
/*
|
||||
* Figure out the name of a default file (in the user's HOME directory).
|
||||
*/
|
||||
char *
|
||||
homefile(filename)
|
||||
char *filename;
|
||||
{
|
||||
char *p;
|
||||
char *pathname;
|
||||
|
||||
if ((p = getenv("HOME")) != NULL && *p != '\0')
|
||||
pathname = mkpathname(p, filename);
|
||||
#if OS2
|
||||
else if ((p = getenv("INIT")) != NULL && *p != '\0')
|
||||
pathname = mkpathname(p, filename);
|
||||
#endif
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "cannot find $HOME - using current directory\n");
|
||||
pathname = mkpathname(".", filename);
|
||||
}
|
||||
return (pathname);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse command line arguments.
|
||||
*/
|
||||
void
|
||||
parse_args(argc, argv)
|
||||
int argc;
|
||||
char **argv;
|
||||
{
|
||||
char *arg;
|
||||
|
||||
outfile = NULL;
|
||||
while (--argc > 0)
|
||||
{
|
||||
arg = *++argv;
|
||||
if (arg[0] != '-')
|
||||
/* Arg does not start with "-"; it's not an option. */
|
||||
break;
|
||||
if (arg[1] == '\0')
|
||||
/* "-" means standard input. */
|
||||
break;
|
||||
if (arg[1] == '-' && arg[2] == '\0')
|
||||
{
|
||||
/* "--" means end of options. */
|
||||
argc--;
|
||||
argv++;
|
||||
break;
|
||||
}
|
||||
switch (arg[1])
|
||||
{
|
||||
case '-':
|
||||
if (strncmp(arg, "--output", 8) == 0)
|
||||
{
|
||||
if (arg[8] == '\0')
|
||||
outfile = &arg[8];
|
||||
else if (arg[8] == '=')
|
||||
outfile = &arg[9];
|
||||
else
|
||||
usage();
|
||||
goto opt_o;
|
||||
}
|
||||
if (strcmp(arg, "--version") == 0)
|
||||
{
|
||||
goto opt_V;
|
||||
}
|
||||
usage();
|
||||
break;
|
||||
case 'o':
|
||||
outfile = &argv[0][2];
|
||||
opt_o:
|
||||
if (*outfile == '\0')
|
||||
{
|
||||
if (--argc <= 0)
|
||||
usage();
|
||||
outfile = *(++argv);
|
||||
}
|
||||
break;
|
||||
case 'V':
|
||||
opt_V:
|
||||
printf("lesskey version %s\n", version);
|
||||
exit(0);
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
if (argc > 1)
|
||||
usage();
|
||||
/*
|
||||
* Open the input file, or use DEF_LESSKEYINFILE if none specified.
|
||||
*/
|
||||
if (argc > 0)
|
||||
infile = *argv;
|
||||
else
|
||||
infile = homefile(DEF_LESSKEYINFILE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize data structures.
|
||||
*/
|
||||
void
|
||||
init_tables()
|
||||
{
|
||||
cmdtable.names = cmdnames;
|
||||
cmdtable.pbuffer = cmdtable.buffer;
|
||||
|
||||
edittable.names = editnames;
|
||||
edittable.pbuffer = edittable.buffer;
|
||||
|
||||
vartable.names = NULL;
|
||||
vartable.pbuffer = vartable.buffer;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse one character of a string.
|
||||
*/
|
||||
char *
|
||||
tstr(pp, xlate)
|
||||
char **pp;
|
||||
int xlate;
|
||||
{
|
||||
register char *p;
|
||||
register char ch;
|
||||
register int i;
|
||||
static char buf[10];
|
||||
static char tstr_control_k[] =
|
||||
{ SK_SPECIAL_KEY, SK_CONTROL_K, 6, 1, 1, 1, '\0' };
|
||||
|
||||
p = *pp;
|
||||
switch (*p)
|
||||
{
|
||||
case '\\':
|
||||
++p;
|
||||
switch (*p)
|
||||
{
|
||||
case '0': case '1': case '2': case '3':
|
||||
case '4': case '5': case '6': case '7':
|
||||
/*
|
||||
* Parse an octal number.
|
||||
*/
|
||||
ch = 0;
|
||||
i = 0;
|
||||
do
|
||||
ch = 8*ch + (*p - '0');
|
||||
while (*++p >= '0' && *p <= '7' && ++i < 3);
|
||||
*pp = p;
|
||||
if (xlate && ch == CONTROL('K'))
|
||||
return tstr_control_k;
|
||||
buf[0] = ch;
|
||||
buf[1] = '\0';
|
||||
return (buf);
|
||||
case 'b':
|
||||
*pp = p+1;
|
||||
return ("\b");
|
||||
case 'e':
|
||||
*pp = p+1;
|
||||
buf[0] = ESC;
|
||||
buf[1] = '\0';
|
||||
return (buf);
|
||||
case 'n':
|
||||
*pp = p+1;
|
||||
return ("\n");
|
||||
case 'r':
|
||||
*pp = p+1;
|
||||
return ("\r");
|
||||
case 't':
|
||||
*pp = p+1;
|
||||
return ("\t");
|
||||
case 'k':
|
||||
if (xlate)
|
||||
{
|
||||
switch (*++p)
|
||||
{
|
||||
case 'u': ch = SK_UP_ARROW; break;
|
||||
case 'd': ch = SK_DOWN_ARROW; break;
|
||||
case 'r': ch = SK_RIGHT_ARROW; break;
|
||||
case 'l': ch = SK_LEFT_ARROW; break;
|
||||
case 'U': ch = SK_PAGE_UP; break;
|
||||
case 'D': ch = SK_PAGE_DOWN; break;
|
||||
case 'h': ch = SK_HOME; break;
|
||||
case 'e': ch = SK_END; break;
|
||||
case 'x': ch = SK_DELETE; break;
|
||||
default:
|
||||
terror("illegal char after \\k");
|
||||
*pp = p+1;
|
||||
return ("");
|
||||
}
|
||||
*pp = p+1;
|
||||
buf[0] = SK_SPECIAL_KEY;
|
||||
buf[1] = ch;
|
||||
buf[2] = 6;
|
||||
buf[3] = 1;
|
||||
buf[4] = 1;
|
||||
buf[5] = 1;
|
||||
buf[6] = '\0';
|
||||
return (buf);
|
||||
}
|
||||
/* FALLTHRU */
|
||||
default:
|
||||
/*
|
||||
* Backslash followed by any other char
|
||||
* just means that char.
|
||||
*/
|
||||
*pp = p+1;
|
||||
buf[0] = *p;
|
||||
buf[1] = '\0';
|
||||
if (xlate && buf[0] == CONTROL('K'))
|
||||
return tstr_control_k;
|
||||
return (buf);
|
||||
}
|
||||
case '^':
|
||||
/*
|
||||
* Carat means CONTROL.
|
||||
*/
|
||||
*pp = p+2;
|
||||
buf[0] = CONTROL(p[1]);
|
||||
buf[1] = '\0';
|
||||
if (buf[0] == CONTROL('K'))
|
||||
return tstr_control_k;
|
||||
return (buf);
|
||||
}
|
||||
*pp = p+1;
|
||||
buf[0] = *p;
|
||||
buf[1] = '\0';
|
||||
if (xlate && buf[0] == CONTROL('K'))
|
||||
return tstr_control_k;
|
||||
return (buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip leading spaces in a string.
|
||||
*/
|
||||
public char *
|
||||
skipsp(s)
|
||||
register char *s;
|
||||
{
|
||||
while (*s == ' ' || *s == '\t')
|
||||
s++;
|
||||
return (s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip non-space characters in a string.
|
||||
*/
|
||||
public char *
|
||||
skipnsp(s)
|
||||
register char *s;
|
||||
{
|
||||
while (*s != '\0' && *s != ' ' && *s != '\t')
|
||||
s++;
|
||||
return (s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up an input line:
|
||||
* strip off the trailing newline & any trailing # comment.
|
||||
*/
|
||||
char *
|
||||
clean_line(s)
|
||||
char *s;
|
||||
{
|
||||
register int i;
|
||||
|
||||
s = skipsp(s);
|
||||
for (i = 0; s[i] != '\n' && s[i] != '\r' && s[i] != '\0'; i++)
|
||||
if (s[i] == '#' && (i == 0 || s[i-1] != '\\'))
|
||||
break;
|
||||
s[i] = '\0';
|
||||
return (s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a byte to the output command table.
|
||||
*/
|
||||
void
|
||||
add_cmd_char(c)
|
||||
int c;
|
||||
{
|
||||
if (currtable->pbuffer >= currtable->buffer + MAX_USERCMD)
|
||||
{
|
||||
terror("too many commands");
|
||||
exit(1);
|
||||
}
|
||||
*(currtable->pbuffer)++ = c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a string to the output command table.
|
||||
*/
|
||||
void
|
||||
add_cmd_str(s)
|
||||
char *s;
|
||||
{
|
||||
for ( ; *s != '\0'; s++)
|
||||
add_cmd_char(*s);
|
||||
}
|
||||
|
||||
/*
|
||||
* See if we have a special "control" line.
|
||||
*/
|
||||
int
|
||||
control_line(s)
|
||||
char *s;
|
||||
{
|
||||
#define PREFIX(str,pat) (strncmp(str,pat,strlen(pat)-1) == 0)
|
||||
|
||||
if (PREFIX(s, "#line-edit"))
|
||||
{
|
||||
currtable = &edittable;
|
||||
return (1);
|
||||
}
|
||||
if (PREFIX(s, "#command"))
|
||||
{
|
||||
currtable = &cmdtable;
|
||||
return (1);
|
||||
}
|
||||
if (PREFIX(s, "#env"))
|
||||
{
|
||||
currtable = &vartable;
|
||||
return (1);
|
||||
}
|
||||
if (PREFIX(s, "#stop"))
|
||||
{
|
||||
add_cmd_char('\0');
|
||||
add_cmd_char(A_END_LIST);
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Output some bytes.
|
||||
*/
|
||||
void
|
||||
fputbytes(fd, buf, len)
|
||||
FILE *fd;
|
||||
char *buf;
|
||||
int len;
|
||||
{
|
||||
while (len-- > 0)
|
||||
{
|
||||
fwrite(buf, sizeof(char), 1, fd);
|
||||
buf++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Output an integer, in special KRADIX form.
|
||||
*/
|
||||
void
|
||||
fputint(fd, val)
|
||||
FILE *fd;
|
||||
unsigned int val;
|
||||
{
|
||||
char c;
|
||||
|
||||
if (val >= KRADIX*KRADIX)
|
||||
{
|
||||
fprintf(stderr, "error: integer too big (%d > %d)\n",
|
||||
val, KRADIX*KRADIX);
|
||||
exit(1);
|
||||
}
|
||||
c = val % KRADIX;
|
||||
fwrite(&c, sizeof(char), 1, fd);
|
||||
c = val / KRADIX;
|
||||
fwrite(&c, sizeof(char), 1, fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find an action, given the name of the action.
|
||||
*/
|
||||
int
|
||||
findaction(actname)
|
||||
char *actname;
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; currtable->names[i].cn_name != NULL; i++)
|
||||
if (strcmp(currtable->names[i].cn_name, actname) == 0)
|
||||
return (currtable->names[i].cn_action);
|
||||
terror("unknown action");
|
||||
return (A_INVALID);
|
||||
}
|
||||
|
||||
void
|
||||
terror(s)
|
||||
char *s;
|
||||
{
|
||||
fprintf(stderr, "line %d: %s\n", linenum, s);
|
||||
errors++;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
parse_cmdline(p)
|
||||
char *p;
|
||||
{
|
||||
int cmdlen;
|
||||
char *actname;
|
||||
int action;
|
||||
char *s;
|
||||
char c;
|
||||
|
||||
/*
|
||||
* Parse the command string and store it in the current table.
|
||||
*/
|
||||
cmdlen = 0;
|
||||
do
|
||||
{
|
||||
s = tstr(&p, 1);
|
||||
cmdlen += strlen(s);
|
||||
if (cmdlen > MAX_CMDLEN)
|
||||
terror("command too long");
|
||||
else
|
||||
add_cmd_str(s);
|
||||
} while (*p != ' ' && *p != '\t' && *p != '\0');
|
||||
/*
|
||||
* Terminate the command string with a null byte.
|
||||
*/
|
||||
add_cmd_char('\0');
|
||||
|
||||
/*
|
||||
* Skip white space between the command string
|
||||
* and the action name.
|
||||
* Terminate the action name with a null byte.
|
||||
*/
|
||||
p = skipsp(p);
|
||||
if (*p == '\0')
|
||||
{
|
||||
terror("missing action");
|
||||
return;
|
||||
}
|
||||
actname = p;
|
||||
p = skipnsp(p);
|
||||
c = *p;
|
||||
*p = '\0';
|
||||
|
||||
/*
|
||||
* Parse the action name and store it in the current table.
|
||||
*/
|
||||
action = findaction(actname);
|
||||
|
||||
/*
|
||||
* See if an extra string follows the action name.
|
||||
*/
|
||||
*p = c;
|
||||
p = skipsp(p);
|
||||
if (*p == '\0')
|
||||
{
|
||||
add_cmd_char(action);
|
||||
} else
|
||||
{
|
||||
/*
|
||||
* OR the special value A_EXTRA into the action byte.
|
||||
* Put the extra string after the action byte.
|
||||
*/
|
||||
add_cmd_char(action | A_EXTRA);
|
||||
while (*p != '\0')
|
||||
add_cmd_str(tstr(&p, 0));
|
||||
add_cmd_char('\0');
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
parse_varline(p)
|
||||
char *p;
|
||||
{
|
||||
char *s;
|
||||
|
||||
do
|
||||
{
|
||||
s = tstr(&p, 0);
|
||||
add_cmd_str(s);
|
||||
} while (*p != ' ' && *p != '\t' && *p != '=' && *p != '\0');
|
||||
/*
|
||||
* Terminate the variable name with a null byte.
|
||||
*/
|
||||
add_cmd_char('\0');
|
||||
|
||||
p = skipsp(p);
|
||||
if (*p++ != '=')
|
||||
{
|
||||
terror("missing =");
|
||||
return;
|
||||
}
|
||||
|
||||
add_cmd_char(EV_OK|A_EXTRA);
|
||||
|
||||
p = skipsp(p);
|
||||
while (*p != '\0')
|
||||
{
|
||||
s = tstr(&p, 0);
|
||||
add_cmd_str(s);
|
||||
}
|
||||
add_cmd_char('\0');
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a line from the lesskey file.
|
||||
*/
|
||||
void
|
||||
parse_line(line)
|
||||
char *line;
|
||||
{
|
||||
char *p;
|
||||
|
||||
/*
|
||||
* See if it is a control line.
|
||||
*/
|
||||
if (control_line(line))
|
||||
return;
|
||||
/*
|
||||
* Skip leading white space.
|
||||
* Replace the final newline with a null byte.
|
||||
* Ignore blank lines and comments.
|
||||
*/
|
||||
p = clean_line(line);
|
||||
if (*p == '\0')
|
||||
return;
|
||||
|
||||
if (currtable == &vartable)
|
||||
parse_varline(p);
|
||||
else
|
||||
parse_cmdline(p);
|
||||
}
|
||||
|
||||
int
|
||||
main(argc, argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
FILE *desc;
|
||||
FILE *out;
|
||||
char line[1024];
|
||||
|
||||
#ifdef WIN32
|
||||
if (getenv("HOME") == NULL)
|
||||
{
|
||||
/*
|
||||
* If there is no HOME environment variable,
|
||||
* try the concatenation of HOMEDRIVE + HOMEPATH.
|
||||
*/
|
||||
char *drive = getenv("HOMEDRIVE");
|
||||
char *path = getenv("HOMEPATH");
|
||||
if (drive != NULL && path != NULL)
|
||||
{
|
||||
char *env = (char *) calloc(strlen(drive) +
|
||||
strlen(path) + 6, sizeof(char));
|
||||
strcpy(env, "HOME=");
|
||||
strcat(env, drive);
|
||||
strcat(env, path);
|
||||
putenv(env);
|
||||
}
|
||||
}
|
||||
#endif /* WIN32 */
|
||||
|
||||
/*
|
||||
* Process command line arguments.
|
||||
*/
|
||||
parse_args(argc, argv);
|
||||
init_tables();
|
||||
|
||||
/*
|
||||
* Open the input file.
|
||||
*/
|
||||
if (strcmp(infile, "-") == 0)
|
||||
desc = stdin;
|
||||
else if ((desc = fopen(infile, "r")) == NULL)
|
||||
{
|
||||
#if HAVE_PERROR
|
||||
perror(infile);
|
||||
#else
|
||||
fprintf(stderr, "Cannot open %s\n", infile);
|
||||
#endif
|
||||
usage();
|
||||
}
|
||||
|
||||
/*
|
||||
* Read and parse the input file, one line at a time.
|
||||
*/
|
||||
errors = 0;
|
||||
linenum = 0;
|
||||
while (fgets(line, sizeof(line), desc) != NULL)
|
||||
{
|
||||
++linenum;
|
||||
parse_line(line);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the output file.
|
||||
* If no output file was specified, use "$HOME/.less"
|
||||
*/
|
||||
if (errors > 0)
|
||||
{
|
||||
fprintf(stderr, "%d errors; no output produced\n", errors);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (outfile == NULL)
|
||||
outfile = getenv("LESSKEY");
|
||||
if (outfile == NULL)
|
||||
outfile = homefile(LESSKEYFILE);
|
||||
if ((out = fopen(outfile, "wb")) == NULL)
|
||||
{
|
||||
#if HAVE_PERROR
|
||||
perror(outfile);
|
||||
#else
|
||||
fprintf(stderr, "Cannot open %s\n", outfile);
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* File header */
|
||||
fputbytes(out, fileheader, sizeof(fileheader));
|
||||
|
||||
/* Command key section */
|
||||
fputbytes(out, cmdsection, sizeof(cmdsection));
|
||||
fputint(out, cmdtable.pbuffer - cmdtable.buffer);
|
||||
fputbytes(out, (char *)cmdtable.buffer, cmdtable.pbuffer-cmdtable.buffer);
|
||||
/* Edit key section */
|
||||
fputbytes(out, editsection, sizeof(editsection));
|
||||
fputint(out, edittable.pbuffer - edittable.buffer);
|
||||
fputbytes(out, (char *)edittable.buffer, edittable.pbuffer-edittable.buffer);
|
||||
|
||||
/* Environment variable section */
|
||||
fputbytes(out, varsection, sizeof(varsection));
|
||||
fputint(out, vartable.pbuffer - vartable.buffer);
|
||||
fputbytes(out, (char *)vartable.buffer, vartable.pbuffer-vartable.buffer);
|
||||
|
||||
/* File trailer */
|
||||
fputbytes(out, endsection, sizeof(endsection));
|
||||
fputbytes(out, filetrailer, sizeof(filetrailer));
|
||||
return (0);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 1984-2004 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 about less, or for information on how to
|
||||
* contact the author, see the README file.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Format of a lesskey file:
|
||||
*
|
||||
* LESSKEY_MAGIC (4 bytes)
|
||||
* sections...
|
||||
* END_LESSKEY_MAGIC (4 bytes)
|
||||
*
|
||||
* Each section is:
|
||||
*
|
||||
* section_MAGIC (1 byte)
|
||||
* section_length (2 bytes)
|
||||
* key table (section_length bytes)
|
||||
*/
|
||||
#define C0_LESSKEY_MAGIC '\0'
|
||||
#define C1_LESSKEY_MAGIC 'M'
|
||||
#define C2_LESSKEY_MAGIC '+'
|
||||
#define C3_LESSKEY_MAGIC 'G'
|
||||
|
||||
#define CMD_SECTION 'c'
|
||||
#define EDIT_SECTION 'e'
|
||||
#define VAR_SECTION 'v'
|
||||
#define END_SECTION 'x'
|
||||
|
||||
#define C0_END_LESSKEY_MAGIC 'E'
|
||||
#define C1_END_LESSKEY_MAGIC 'n'
|
||||
#define C2_END_LESSKEY_MAGIC 'd'
|
||||
|
||||
/* */
|
||||
#define KRADIX 64
|
||||
Reference in New Issue
Block a user