lesskey.c revision 60786
160786Sps/*
260786Sps * Copyright (C) 1984-2000  Mark Nudelman
360786Sps *
460786Sps * You may distribute under the terms of either the GNU General Public
560786Sps * License or the Less License, as specified in the README file.
660786Sps *
760786Sps * For more information about less, or for information on how to
860786Sps * contact the author, see the README file.
960786Sps */
1060786Sps
1160786Sps
1260786Sps/*
1360786Sps *	lesskey [-o output] [input]
1460786Sps *
1560786Sps * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1660786Sps *
1760786Sps *	Make a .less file.
1860786Sps *	If no input file is specified, standard input is used.
1960786Sps *	If no output file is specified, $HOME/.less is used.
2060786Sps *
2160786Sps *	The .less file is used to specify (to "less") user-defined
2260786Sps *	key bindings.  Basically any sequence of 1 to MAX_CMDLEN
2360786Sps *	keystrokes may be bound to an existing less function.
2460786Sps *
2560786Sps * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2660786Sps *
2760786Sps *	The input file is an ascii file consisting of a
2860786Sps *	sequence of lines of the form:
2960786Sps *		string <whitespace> action [chars] <newline>
3060786Sps *
3160786Sps *	"string" is a sequence of command characters which form
3260786Sps *		the new user-defined command.  The command
3360786Sps *		characters may be:
3460786Sps *		1. The actual character itself.
3560786Sps *		2. A character preceded by ^ to specify a
3660786Sps *		   control character (e.g. ^X means control-X).
3760786Sps *		3. A backslash followed by one to three octal digits
3860786Sps *		   to specify a character by its octal value.
3960786Sps *		4. A backslash followed by b, e, n, r or t
4060786Sps *		   to specify \b, ESC, \n, \r or \t, respectively.
4160786Sps *		5. Any character (other than those mentioned above) preceded
4260786Sps *		   by a \ to specify the character itself (characters which
4360786Sps *		   must be preceded by \ include ^, \, and whitespace.
4460786Sps *	"action" is the name of a "less" action, from the table below.
4560786Sps *	"chars" is an optional sequence of characters which is treated
4660786Sps *		as keyboard input after the command is executed.
4760786Sps *
4860786Sps *	Blank lines and lines which start with # are ignored,
4960786Sps *	except for the special control lines:
5060786Sps *		#command	Signals the beginning of the command
5160786Sps *				keys section.
5260786Sps *		#line-edit	Signals the beginning of the line-editing
5360786Sps *				keys section.
5460786Sps *		#env		Signals the beginning of the environment
5560786Sps *				variable section.
5660786Sps *		#stop		Stops command parsing in less;
5760786Sps *				causes all default keys to be disabled.
5860786Sps *
5960786Sps * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6060786Sps *
6160786Sps *	The output file is a non-ascii file, consisting of a header,
6260786Sps *	one or more sections, and a trailer.
6360786Sps *	Each section begins with a section header, a section length word
6460786Sps *	and the section data.  Normally there are three sections:
6560786Sps *		CMD_SECTION	Definition of command keys.
6660786Sps *		EDIT_SECTION	Definition of editing keys.
6760786Sps *		END_SECTION	A special section header, with no
6860786Sps *				length word or section data.
6960786Sps *
7060786Sps *	Section data consists of zero or more byte sequences of the form:
7160786Sps *		string <0> <action>
7260786Sps *	or
7360786Sps *		string <0> <action|A_EXTRA> chars <0>
7460786Sps *
7560786Sps *	"string" is the command string.
7660786Sps *	"<0>" is one null byte.
7760786Sps *	"<action>" is one byte containing the action code (the A_xxx value).
7860786Sps *	If action is ORed with A_EXTRA, the action byte is followed
7960786Sps *		by the null-terminated "chars" string.
8060786Sps *
8160786Sps * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8260786Sps */
8360786Sps
8460786Sps#include "less.h"
8560786Sps#include "lesskey.h"
8660786Sps#include "cmd.h"
8760786Sps
8860786Spsstruct cmdname
8960786Sps{
9060786Sps	char *cn_name;
9160786Sps	int cn_action;
9260786Sps};
9360786Sps
9460786Spsstruct cmdname cmdnames[] =
9560786Sps{
9660786Sps	"back-bracket",		A_B_BRACKET,
9760786Sps	"back-line",		A_B_LINE,
9860786Sps	"back-line-force",	A_BF_LINE,
9960786Sps	"back-screen",		A_B_SCREEN,
10060786Sps	"back-scroll",		A_B_SCROLL,
10160786Sps	"back-search",		A_B_SEARCH,
10260786Sps	"back-window",		A_B_WINDOW,
10360786Sps	"debug",		A_DEBUG,
10460786Sps	"display-flag",		A_DISP_OPTION,
10560786Sps	"display-option",	A_DISP_OPTION,
10660786Sps	"end",			A_GOEND,
10760786Sps	"examine",		A_EXAMINE,
10860786Sps	"first-cmd",		A_FIRSTCMD,
10960786Sps	"firstcmd",		A_FIRSTCMD,
11060786Sps	"flush-repaint",	A_FREPAINT,
11160786Sps	"forw-bracket",		A_F_BRACKET,
11260786Sps	"forw-forever",		A_F_FOREVER,
11360786Sps	"forw-line",		A_F_LINE,
11460786Sps	"forw-line-force",	A_FF_LINE,
11560786Sps	"forw-screen",		A_F_SCREEN,
11660786Sps	"forw-screen-force",	A_FF_SCREEN,
11760786Sps	"forw-scroll",		A_F_SCROLL,
11860786Sps	"forw-search",		A_F_SEARCH,
11960786Sps	"forw-window",		A_F_WINDOW,
12060786Sps	"goto-end",		A_GOEND,
12160786Sps	"goto-line",		A_GOLINE,
12260786Sps	"goto-mark",		A_GOMARK,
12360786Sps	"help",			A_HELP,
12460786Sps	"index-file",		A_INDEX_FILE,
12560786Sps	"invalid",		A_UINVALID,
12660786Sps	"left-scroll",		A_LSHIFT,
12760786Sps	"next-file",		A_NEXT_FILE,
12860786Sps	"noaction",		A_NOACTION,
12960786Sps	"percent",		A_PERCENT,
13060786Sps	"pipe",			A_PIPE,
13160786Sps	"prev-file",		A_PREV_FILE,
13260786Sps	"quit",			A_QUIT,
13360786Sps	"repaint",		A_REPAINT,
13460786Sps	"repaint-flush",	A_FREPAINT,
13560786Sps	"repeat-search",	A_AGAIN_SEARCH,
13660786Sps	"repeat-search-all",	A_T_AGAIN_SEARCH,
13760786Sps	"reverse-search",	A_REVERSE_SEARCH,
13860786Sps	"reverse-search-all",	A_T_REVERSE_SEARCH,
13960786Sps	"right-scroll",		A_RSHIFT,
14060786Sps	"set-mark",		A_SETMARK,
14160786Sps	"shell",		A_SHELL,
14260786Sps	"status",		A_STAT,
14360786Sps	"toggle-flag",		A_OPT_TOGGLE,
14460786Sps	"toggle-option",	A_OPT_TOGGLE,
14560786Sps	"undo-hilite",		A_UNDO_SEARCH,
14660786Sps	"version",		A_VERSION,
14760786Sps	"visual",		A_VISUAL,
14860786Sps	NULL,			0
14960786Sps};
15060786Sps
15160786Spsstruct cmdname editnames[] =
15260786Sps{
15360786Sps	"back-complete",	EC_B_COMPLETE,
15460786Sps	"backspace",		EC_BACKSPACE,
15560786Sps	"delete",		EC_DELETE,
15660786Sps	"down",			EC_DOWN,
15760786Sps	"end",			EC_END,
15860786Sps	"expand",		EC_EXPAND,
15960786Sps	"forw-complete",	EC_F_COMPLETE,
16060786Sps	"home",			EC_HOME,
16160786Sps	"insert",		EC_INSERT,
16260786Sps	"invalid",		EC_UINVALID,
16360786Sps	"kill-line",		EC_LINEKILL,
16460786Sps	"left",			EC_LEFT,
16560786Sps	"literal",		EC_LITERAL,
16660786Sps	"right",		EC_RIGHT,
16760786Sps	"up",			EC_UP,
16860786Sps	"word-backspace",	EC_W_BACKSPACE,
16960786Sps	"word-delete",		EC_W_DELETE,
17060786Sps	"word-left",		EC_W_LEFT,
17160786Sps	"word-right",		EC_W_RIGHT,
17260786Sps	NULL,			0
17360786Sps};
17460786Sps
17560786Spsstruct table
17660786Sps{
17760786Sps	struct cmdname *names;
17860786Sps	char *pbuffer;
17960786Sps	char buffer[MAX_USERCMD];
18060786Sps};
18160786Sps
18260786Spsstruct table cmdtable;
18360786Spsstruct table edittable;
18460786Spsstruct table vartable;
18560786Spsstruct table *currtable = &cmdtable;
18660786Sps
18760786Spschar fileheader[] = {
18860786Sps	C0_LESSKEY_MAGIC,
18960786Sps	C1_LESSKEY_MAGIC,
19060786Sps	C2_LESSKEY_MAGIC,
19160786Sps	C3_LESSKEY_MAGIC
19260786Sps};
19360786Spschar filetrailer[] = {
19460786Sps	C0_END_LESSKEY_MAGIC,
19560786Sps	C1_END_LESSKEY_MAGIC,
19660786Sps	C2_END_LESSKEY_MAGIC
19760786Sps};
19860786Spschar cmdsection[1] =	{ CMD_SECTION };
19960786Spschar editsection[1] =	{ EDIT_SECTION };
20060786Spschar varsection[1] =	{ VAR_SECTION };
20160786Spschar endsection[1] =	{ END_SECTION };
20260786Sps
20360786Spschar *infile = NULL;
20460786Spschar *outfile = NULL ;
20560786Sps
20660786Spsint linenum;
20760786Spsint errors;
20860786Sps
20960786Spsextern char version[];
21060786Sps
21160786Sps	void
21260786Spsusage()
21360786Sps{
21460786Sps	fprintf(stderr, "usage: lesskey [-o output] [input]\n");
21560786Sps	exit(1);
21660786Sps}
21760786Sps
21860786Sps	char *
21960786Spsmkpathname(dirname, filename)
22060786Sps	char *dirname;
22160786Sps	char *filename;
22260786Sps{
22360786Sps	char *pathname;
22460786Sps
22560786Sps	pathname = calloc(strlen(dirname) + strlen(filename) + 2, sizeof(char));
22660786Sps	strcpy(pathname, dirname);
22760786Sps	strcat(pathname, PATHNAME_SEP);
22860786Sps	strcat(pathname, filename);
22960786Sps	return (pathname);
23060786Sps}
23160786Sps
23260786Sps/*
23360786Sps * Figure out the name of a default file (in the user's HOME directory).
23460786Sps */
23560786Sps	char *
23660786Spshomefile(filename)
23760786Sps	char *filename;
23860786Sps{
23960786Sps	char *p;
24060786Sps	char *pathname;
24160786Sps
24260786Sps	if ((p = getenv("HOME")) != NULL && *p != '\0')
24360786Sps		pathname = mkpathname(p, filename);
24460786Sps#if OS2
24560786Sps	else if ((p = getenv("INIT")) != NULL && *p != '\0')
24660786Sps		pathname = mkpathname(p, filename);
24760786Sps#endif
24860786Sps	else
24960786Sps	{
25060786Sps		fprintf(stderr, "cannot find $HOME - using current directory\n");
25160786Sps		pathname = mkpathname(".", filename);
25260786Sps	}
25360786Sps	return (pathname);
25460786Sps}
25560786Sps
25660786Sps/*
25760786Sps * Parse command line arguments.
25860786Sps */
25960786Sps	void
26060786Spsparse_args(argc, argv)
26160786Sps	int argc;
26260786Sps	char **argv;
26360786Sps{
26460786Sps	char *arg;
26560786Sps
26660786Sps	outfile = NULL;
26760786Sps	while (--argc > 0)
26860786Sps	{
26960786Sps		arg = *++argv;
27060786Sps		if (arg[0] != '-')
27160786Sps			/* Arg does not start with "-"; it's not an option. */
27260786Sps			break;
27360786Sps		if (arg[1] == '\0')
27460786Sps			/* "-" means standard input. */
27560786Sps			break;
27660786Sps		if (arg[1] == '-' && arg[2] == '\0')
27760786Sps		{
27860786Sps			/* "--" means end of options. */
27960786Sps			argc--;
28060786Sps			argv++;
28160786Sps			break;
28260786Sps		}
28360786Sps		switch (arg[1])
28460786Sps		{
28560786Sps		case '-':
28660786Sps			if (strncmp(arg, "--output", 8) == 0)
28760786Sps			{
28860786Sps				if (arg[8] == '\0')
28960786Sps					outfile = &arg[8];
29060786Sps				else if (arg[8] == '=')
29160786Sps					outfile = &arg[9];
29260786Sps				else
29360786Sps					usage();
29460786Sps				goto opt_o;
29560786Sps			}
29660786Sps			if (strcmp(arg, "--version") == 0)
29760786Sps			{
29860786Sps				goto opt_V;
29960786Sps			}
30060786Sps			usage();
30160786Sps			break;
30260786Sps		case 'o':
30360786Sps			outfile = &argv[0][2];
30460786Sps		opt_o:
30560786Sps			if (*outfile == '\0')
30660786Sps			{
30760786Sps				if (--argc <= 0)
30860786Sps					usage();
30960786Sps				outfile = *(++argv);
31060786Sps			}
31160786Sps			break;
31260786Sps		case 'V':
31360786Sps		opt_V:
31460786Sps			printf("lesskey  version %s\n", version);
31560786Sps			exit(0);
31660786Sps		default:
31760786Sps			usage();
31860786Sps		}
31960786Sps	}
32060786Sps	if (argc > 1)
32160786Sps		usage();
32260786Sps	/*
32360786Sps	 * Open the input file, or use DEF_LESSKEYINFILE if none specified.
32460786Sps	 */
32560786Sps	if (argc > 0)
32660786Sps		infile = *argv;
32760786Sps	else
32860786Sps		infile = homefile(DEF_LESSKEYINFILE);
32960786Sps}
33060786Sps
33160786Sps/*
33260786Sps * Initialize data structures.
33360786Sps */
33460786Sps	void
33560786Spsinit_tables()
33660786Sps{
33760786Sps	cmdtable.names = cmdnames;
33860786Sps	cmdtable.pbuffer = cmdtable.buffer;
33960786Sps
34060786Sps	edittable.names = editnames;
34160786Sps	edittable.pbuffer = edittable.buffer;
34260786Sps
34360786Sps	vartable.names = NULL;
34460786Sps	vartable.pbuffer = vartable.buffer;
34560786Sps}
34660786Sps
34760786Sps/*
34860786Sps * Parse one character of a string.
34960786Sps */
35060786Sps	char *
35160786Spststr(pp)
35260786Sps	char **pp;
35360786Sps{
35460786Sps	register char *p;
35560786Sps	register char ch;
35660786Sps	register int i;
35760786Sps	static char buf[10];
35860786Sps	static char tstr_control_k[] =
35960786Sps		{ SK_SPECIAL_KEY, SK_CONTROL_K, 6, 1, 1, 1, '\0' };
36060786Sps
36160786Sps	p = *pp;
36260786Sps	switch (*p)
36360786Sps	{
36460786Sps	case '\\':
36560786Sps		++p;
36660786Sps		switch (*p)
36760786Sps		{
36860786Sps		case '0': case '1': case '2': case '3':
36960786Sps		case '4': case '5': case '6': case '7':
37060786Sps			/*
37160786Sps			 * Parse an octal number.
37260786Sps			 */
37360786Sps			ch = 0;
37460786Sps			i = 0;
37560786Sps			do
37660786Sps				ch = 8*ch + (*p - '0');
37760786Sps			while (*++p >= '0' && *p <= '7' && ++i < 3);
37860786Sps			*pp = p;
37960786Sps			if (ch == CONTROL('K'))
38060786Sps				return tstr_control_k;
38160786Sps			buf[0] = ch;
38260786Sps			buf[1] = '\0';
38360786Sps			return (buf);
38460786Sps		case 'b':
38560786Sps			*pp = p+1;
38660786Sps			return ("\b");
38760786Sps		case 'e':
38860786Sps			*pp = p+1;
38960786Sps			buf[0] = ESC;
39060786Sps			buf[1] = '\0';
39160786Sps			return (buf);
39260786Sps		case 'n':
39360786Sps			*pp = p+1;
39460786Sps			return ("\n");
39560786Sps		case 'r':
39660786Sps			*pp = p+1;
39760786Sps			return ("\r");
39860786Sps		case 't':
39960786Sps			*pp = p+1;
40060786Sps			return ("\t");
40160786Sps		case 'k':
40260786Sps			switch (*++p)
40360786Sps			{
40460786Sps			case 'u': ch = SK_UP_ARROW; break;
40560786Sps			case 'd': ch = SK_DOWN_ARROW; break;
40660786Sps			case 'r': ch = SK_RIGHT_ARROW; break;
40760786Sps			case 'l': ch = SK_LEFT_ARROW; break;
40860786Sps			case 'U': ch = SK_PAGE_UP; break;
40960786Sps			case 'D': ch = SK_PAGE_DOWN; break;
41060786Sps			case 'h': ch = SK_HOME; break;
41160786Sps			case 'e': ch = SK_END; break;
41260786Sps			case 'x': ch = SK_DELETE; break;
41360786Sps			}
41460786Sps			*pp = p+1;
41560786Sps			buf[0] = SK_SPECIAL_KEY;
41660786Sps			buf[1] = ch;
41760786Sps			buf[2] = 6;
41860786Sps			buf[3] = 1;
41960786Sps			buf[4] = 1;
42060786Sps			buf[5] = 1;
42160786Sps			buf[6] = '\0';
42260786Sps			return (buf);
42360786Sps		default:
42460786Sps			/*
42560786Sps			 * Backslash followed by any other char
42660786Sps			 * just means that char.
42760786Sps			 */
42860786Sps			*pp = p+1;
42960786Sps			buf[0] = *p;
43060786Sps			buf[1] = '\0';
43160786Sps			if (buf[0] == CONTROL('K'))
43260786Sps				return tstr_control_k;
43360786Sps			return (buf);
43460786Sps		}
43560786Sps	case '^':
43660786Sps		/*
43760786Sps		 * Carat means CONTROL.
43860786Sps		 */
43960786Sps		*pp = p+2;
44060786Sps		buf[0] = CONTROL(p[1]);
44160786Sps		buf[1] = '\0';
44260786Sps		if (buf[0] == CONTROL('K'))
44360786Sps			return tstr_control_k;
44460786Sps		return (buf);
44560786Sps	}
44660786Sps	*pp = p+1;
44760786Sps	buf[0] = *p;
44860786Sps	buf[1] = '\0';
44960786Sps	if (buf[0] == CONTROL('K'))
45060786Sps		return tstr_control_k;
45160786Sps	return (buf);
45260786Sps}
45360786Sps
45460786Sps/*
45560786Sps * Skip leading spaces in a string.
45660786Sps */
45760786Sps	public char *
45860786Spsskipsp(s)
45960786Sps	register char *s;
46060786Sps{
46160786Sps	while (*s == ' ' || *s == '\t')
46260786Sps		s++;
46360786Sps	return (s);
46460786Sps}
46560786Sps
46660786Sps/*
46760786Sps * Skip non-space characters in a string.
46860786Sps */
46960786Sps	public char *
47060786Spsskipnsp(s)
47160786Sps	register char *s;
47260786Sps{
47360786Sps	while (*s != '\0' && *s != ' ' && *s != '\t')
47460786Sps		s++;
47560786Sps	return (s);
47660786Sps}
47760786Sps
47860786Sps/*
47960786Sps * Clean up an input line:
48060786Sps * strip off the trailing newline & any trailing # comment.
48160786Sps */
48260786Sps	char *
48360786Spsclean_line(s)
48460786Sps	char *s;
48560786Sps{
48660786Sps	register int i;
48760786Sps
48860786Sps	s = skipsp(s);
48960786Sps	for (i = 0;  s[i] != '\n' && s[i] != '\r' && s[i] != '\0';  i++)
49060786Sps		if (s[i] == '#' && (i == 0 || s[i-1] != '\\'))
49160786Sps			break;
49260786Sps	s[i] = '\0';
49360786Sps	return (s);
49460786Sps}
49560786Sps
49660786Sps/*
49760786Sps * Add a byte to the output command table.
49860786Sps */
49960786Sps	void
50060786Spsadd_cmd_char(c)
50160786Sps	int c;
50260786Sps{
50360786Sps	if (currtable->pbuffer >= currtable->buffer + MAX_USERCMD)
50460786Sps	{
50560786Sps		error("too many commands");
50660786Sps		exit(1);
50760786Sps	}
50860786Sps	*(currtable->pbuffer)++ = c;
50960786Sps}
51060786Sps
51160786Sps/*
51260786Sps * Add a string to the output command table.
51360786Sps */
51460786Sps	void
51560786Spsadd_cmd_str(s)
51660786Sps	char *s;
51760786Sps{
51860786Sps	for ( ;  *s != '\0';  s++)
51960786Sps		add_cmd_char(*s);
52060786Sps}
52160786Sps
52260786Sps/*
52360786Sps * See if we have a special "control" line.
52460786Sps */
52560786Sps	int
52660786Spscontrol_line(s)
52760786Sps	char *s;
52860786Sps{
52960786Sps#define	PREFIX(str,pat)	(strncmp(str,pat,strlen(pat)-1) == 0)
53060786Sps
53160786Sps	if (PREFIX(s, "#line-edit"))
53260786Sps	{
53360786Sps		currtable = &edittable;
53460786Sps		return (1);
53560786Sps	}
53660786Sps	if (PREFIX(s, "#command"))
53760786Sps	{
53860786Sps		currtable = &cmdtable;
53960786Sps		return (1);
54060786Sps	}
54160786Sps	if (PREFIX(s, "#env"))
54260786Sps	{
54360786Sps		currtable = &vartable;
54460786Sps		return (1);
54560786Sps	}
54660786Sps	if (PREFIX(s, "#stop"))
54760786Sps	{
54860786Sps		add_cmd_char('\0');
54960786Sps		add_cmd_char(A_END_LIST);
55060786Sps		return (1);
55160786Sps	}
55260786Sps	return (0);
55360786Sps}
55460786Sps
55560786Sps/*
55660786Sps * Output some bytes.
55760786Sps */
55860786Sps	void
55960786Spsfputbytes(fd, buf, len)
56060786Sps	FILE *fd;
56160786Sps	char *buf;
56260786Sps	int len;
56360786Sps{
56460786Sps	while (len-- > 0)
56560786Sps	{
56660786Sps		fwrite(buf, sizeof(char), 1, fd);
56760786Sps		buf++;
56860786Sps	}
56960786Sps}
57060786Sps
57160786Sps/*
57260786Sps * Output an integer, in special KRADIX form.
57360786Sps */
57460786Sps	void
57560786Spsfputint(fd, val)
57660786Sps	FILE *fd;
57760786Sps	unsigned int val;
57860786Sps{
57960786Sps	char c;
58060786Sps
58160786Sps	if (val >= KRADIX*KRADIX)
58260786Sps	{
58360786Sps		fprintf(stderr, "error: integer too big (%d > %d)\n",
58460786Sps			val, KRADIX*KRADIX);
58560786Sps		exit(1);
58660786Sps	}
58760786Sps	c = val % KRADIX;
58860786Sps	fwrite(&c, sizeof(char), 1, fd);
58960786Sps	c = val / KRADIX;
59060786Sps	fwrite(&c, sizeof(char), 1, fd);
59160786Sps}
59260786Sps
59360786Sps/*
59460786Sps * Find an action, given the name of the action.
59560786Sps */
59660786Sps	int
59760786Spsfindaction(actname)
59860786Sps	char *actname;
59960786Sps{
60060786Sps	int i;
60160786Sps
60260786Sps	for (i = 0;  currtable->names[i].cn_name != NULL;  i++)
60360786Sps		if (strcmp(currtable->names[i].cn_name, actname) == 0)
60460786Sps			return (currtable->names[i].cn_action);
60560786Sps	error("unknown action");
60660786Sps	return (A_INVALID);
60760786Sps}
60860786Sps
60960786Sps	void
61060786Spserror(s)
61160786Sps	char *s;
61260786Sps{
61360786Sps	fprintf(stderr, "line %d: %s\n", linenum, s);
61460786Sps	errors++;
61560786Sps}
61660786Sps
61760786Sps
61860786Sps	void
61960786Spsparse_cmdline(p)
62060786Sps	char *p;
62160786Sps{
62260786Sps	int cmdlen;
62360786Sps	char *actname;
62460786Sps	int action;
62560786Sps	char *s;
62660786Sps	char c;
62760786Sps
62860786Sps	/*
62960786Sps	 * Parse the command string and store it in the current table.
63060786Sps	 */
63160786Sps	cmdlen = 0;
63260786Sps	do
63360786Sps	{
63460786Sps		s = tstr(&p);
63560786Sps		cmdlen += strlen(s);
63660786Sps		if (cmdlen > MAX_CMDLEN)
63760786Sps			error("command too long");
63860786Sps		else
63960786Sps			add_cmd_str(s);
64060786Sps	} while (*p != ' ' && *p != '\t' && *p != '\0');
64160786Sps	/*
64260786Sps	 * Terminate the command string with a null byte.
64360786Sps	 */
64460786Sps	add_cmd_char('\0');
64560786Sps
64660786Sps	/*
64760786Sps	 * Skip white space between the command string
64860786Sps	 * and the action name.
64960786Sps	 * Terminate the action name with a null byte.
65060786Sps	 */
65160786Sps	p = skipsp(p);
65260786Sps	if (*p == '\0')
65360786Sps	{
65460786Sps		error("missing action");
65560786Sps		return;
65660786Sps	}
65760786Sps	actname = p;
65860786Sps	p = skipnsp(p);
65960786Sps	c = *p;
66060786Sps	*p = '\0';
66160786Sps
66260786Sps	/*
66360786Sps	 * Parse the action name and store it in the current table.
66460786Sps	 */
66560786Sps	action = findaction(actname);
66660786Sps
66760786Sps	/*
66860786Sps	 * See if an extra string follows the action name.
66960786Sps	 */
67060786Sps	*p = c;
67160786Sps	p = skipsp(p);
67260786Sps	if (*p == '\0')
67360786Sps	{
67460786Sps		add_cmd_char(action);
67560786Sps	} else
67660786Sps	{
67760786Sps		/*
67860786Sps		 * OR the special value A_EXTRA into the action byte.
67960786Sps		 * Put the extra string after the action byte.
68060786Sps		 */
68160786Sps		add_cmd_char(action | A_EXTRA);
68260786Sps		while (*p != '\0')
68360786Sps			add_cmd_str(tstr(&p));
68460786Sps		add_cmd_char('\0');
68560786Sps	}
68660786Sps}
68760786Sps
68860786Sps	void
68960786Spsparse_varline(p)
69060786Sps	char *p;
69160786Sps{
69260786Sps	char *s;
69360786Sps
69460786Sps	do
69560786Sps	{
69660786Sps		s = tstr(&p);
69760786Sps		add_cmd_str(s);
69860786Sps	} while (*p != ' ' && *p != '\t' && *p != '=' && *p != '\0');
69960786Sps	/*
70060786Sps	 * Terminate the variable name with a null byte.
70160786Sps	 */
70260786Sps	add_cmd_char('\0');
70360786Sps
70460786Sps	p = skipsp(p);
70560786Sps	if (*p++ != '=')
70660786Sps	{
70760786Sps		error("missing =");
70860786Sps		return;
70960786Sps	}
71060786Sps
71160786Sps	add_cmd_char(EV_OK|A_EXTRA);
71260786Sps
71360786Sps	p = skipsp(p);
71460786Sps	while (*p != '\0')
71560786Sps	{
71660786Sps		s = tstr(&p);
71760786Sps		add_cmd_str(s);
71860786Sps	}
71960786Sps	add_cmd_char('\0');
72060786Sps}
72160786Sps
72260786Sps/*
72360786Sps * Parse a line from the lesskey file.
72460786Sps */
72560786Sps	void
72660786Spsparse_line(line)
72760786Sps	char *line;
72860786Sps{
72960786Sps	char *p;
73060786Sps
73160786Sps	/*
73260786Sps	 * See if it is a control line.
73360786Sps	 */
73460786Sps	if (control_line(line))
73560786Sps		return;
73660786Sps	/*
73760786Sps	 * Skip leading white space.
73860786Sps	 * Replace the final newline with a null byte.
73960786Sps	 * Ignore blank lines and comments.
74060786Sps	 */
74160786Sps	p = clean_line(line);
74260786Sps	if (*p == '\0')
74360786Sps		return;
74460786Sps
74560786Sps	if (currtable == &vartable)
74660786Sps		parse_varline(p);
74760786Sps	else
74860786Sps		parse_cmdline(p);
74960786Sps}
75060786Sps
75160786Sps	int
75260786Spsmain(argc, argv)
75360786Sps	int argc;
75460786Sps	char *argv[];
75560786Sps{
75660786Sps	FILE *desc;
75760786Sps	FILE *out;
75860786Sps	char line[200];
75960786Sps
76060786Sps#ifdef WIN32
76160786Sps	if (getenv("HOME") == NULL)
76260786Sps	{
76360786Sps		/*
76460786Sps		 * If there is no HOME environment variable,
76560786Sps		 * try the concatenation of HOMEDRIVE + HOMEPATH.
76660786Sps		 */
76760786Sps		char *drive = getenv("HOMEDRIVE");
76860786Sps		char *path  = getenv("HOMEPATH");
76960786Sps		if (drive != NULL && path != NULL)
77060786Sps		{
77160786Sps			char *env = (char *) calloc(strlen(drive) +
77260786Sps					strlen(path) + 6, sizeof(char));
77360786Sps			strcpy(env, "HOME=");
77460786Sps			strcat(env, drive);
77560786Sps			strcat(env, path);
77660786Sps			putenv(env);
77760786Sps		}
77860786Sps	}
77960786Sps#endif /* WIN32 */
78060786Sps
78160786Sps	/*
78260786Sps	 * Process command line arguments.
78360786Sps	 */
78460786Sps	parse_args(argc, argv);
78560786Sps	init_tables();
78660786Sps
78760786Sps	/*
78860786Sps	 * Open the input file.
78960786Sps	 */
79060786Sps	if (strcmp(infile, "-") == 0)
79160786Sps		desc = stdin;
79260786Sps	else if ((desc = fopen(infile, "r")) == NULL)
79360786Sps	{
79460786Sps#if HAVE_PERROR
79560786Sps		perror(infile);
79660786Sps#else
79760786Sps		fprintf(stderr, "Cannot open %s\n", infile);
79860786Sps#endif
79960786Sps		usage();
80060786Sps	}
80160786Sps
80260786Sps	/*
80360786Sps	 * Read and parse the input file, one line at a time.
80460786Sps	 */
80560786Sps	errors = 0;
80660786Sps	linenum = 0;
80760786Sps	while (fgets(line, sizeof(line), desc) != NULL)
80860786Sps	{
80960786Sps		++linenum;
81060786Sps		parse_line(line);
81160786Sps	}
81260786Sps
81360786Sps	/*
81460786Sps	 * Write the output file.
81560786Sps	 * If no output file was specified, use "$HOME/.less"
81660786Sps	 */
81760786Sps	if (errors > 0)
81860786Sps	{
81960786Sps		fprintf(stderr, "%d errors; no output produced\n", errors);
82060786Sps		exit(1);
82160786Sps	}
82260786Sps
82360786Sps	if (outfile == NULL)
82460786Sps		outfile = getenv("LESSKEY");
82560786Sps	if (outfile == NULL)
82660786Sps		outfile = homefile(LESSKEYFILE);
82760786Sps	if ((out = fopen(outfile, "wb")) == NULL)
82860786Sps	{
82960786Sps#if HAVE_PERROR
83060786Sps		perror(outfile);
83160786Sps#else
83260786Sps		fprintf(stderr, "Cannot open %s\n", outfile);
83360786Sps#endif
83460786Sps		exit(1);
83560786Sps	}
83660786Sps
83760786Sps	/* File header */
83860786Sps	fputbytes(out, fileheader, sizeof(fileheader));
83960786Sps
84060786Sps	/* Command key section */
84160786Sps	fputbytes(out, cmdsection, sizeof(cmdsection));
84260786Sps	fputint(out, cmdtable.pbuffer - cmdtable.buffer);
84360786Sps	fputbytes(out, (char *)cmdtable.buffer, cmdtable.pbuffer-cmdtable.buffer);
84460786Sps	/* Edit key section */
84560786Sps	fputbytes(out, editsection, sizeof(editsection));
84660786Sps	fputint(out, edittable.pbuffer - edittable.buffer);
84760786Sps	fputbytes(out, (char *)edittable.buffer, edittable.pbuffer-edittable.buffer);
84860786Sps
84960786Sps	/* Environment variable section */
85060786Sps	fputbytes(out, varsection, sizeof(varsection));
85160786Sps	fputint(out, vartable.pbuffer - vartable.buffer);
85260786Sps	fputbytes(out, (char *)vartable.buffer, vartable.pbuffer-vartable.buffer);
85360786Sps
85460786Sps	/* File trailer */
85560786Sps	fputbytes(out, endsection, sizeof(endsection));
85660786Sps	fputbytes(out, filetrailer, sizeof(filetrailer));
85760786Sps	return (0);
85860786Sps}
859