xref: /netbsd-src/external/bsd/less/dist/lesskey.c (revision 3d8a4f7f3d1d774d1aa4b3c44be92310fad81874)
1*3d8a4f7fSsimonb /*	$NetBSD: lesskey.c,v 1.6 2023/10/06 07:26:47 simonb Exp $	*/
220006a0bStron 
320006a0bStron /*
4838f5788Ssimonb  * Copyright (C) 1984-2023  Mark Nudelman
520006a0bStron  *
620006a0bStron  * You may distribute under the terms of either the GNU General Public
720006a0bStron  * License or the Less License, as specified in the README file.
820006a0bStron  *
9ec18bca0Stron  * For more information, see the README file.
1020006a0bStron  */
1120006a0bStron 
1220006a0bStron 
1320006a0bStron /*
1420006a0bStron  *  lesskey [-o output] [input]
1520006a0bStron  *
1620006a0bStron  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1720006a0bStron  *
1820006a0bStron  *  Make a .less file.
1920006a0bStron  *  If no input file is specified, standard input is used.
2020006a0bStron  *  If no output file is specified, $HOME/.less is used.
2120006a0bStron  *
2220006a0bStron  *  The .less file is used to specify (to "less") user-defined
2320006a0bStron  *  key bindings.  Basically any sequence of 1 to MAX_CMDLEN
2420006a0bStron  *  keystrokes may be bound to an existing less function.
2520006a0bStron  *
2620006a0bStron  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2720006a0bStron  *
2820006a0bStron  *  The input file is an ascii file consisting of a
2920006a0bStron  *  sequence of lines of the form:
3020006a0bStron  *          string <whitespace> action [chars] <newline>
3120006a0bStron  *
3220006a0bStron  *      "string" is a sequence of command characters which form
3320006a0bStron  *              the new user-defined command.  The command
3420006a0bStron  *              characters may be:
3520006a0bStron  *              1. The actual character itself.
3620006a0bStron  *              2. A character preceded by ^ to specify a
3720006a0bStron  *                 control character (e.g. ^X means control-X).
3820006a0bStron  *              3. A backslash followed by one to three octal digits
3920006a0bStron  *                 to specify a character by its octal value.
4020006a0bStron  *              4. A backslash followed by b, e, n, r or t
4120006a0bStron  *                 to specify \b, ESC, \n, \r or \t, respectively.
4220006a0bStron  *              5. Any character (other than those mentioned above) preceded
4320006a0bStron  *                 by a \ to specify the character itself (characters which
4420006a0bStron  *                 must be preceded by \ include ^, \, and whitespace.
4520006a0bStron  *      "action" is the name of a "less" action, from the table below.
4620006a0bStron  *      "chars" is an optional sequence of characters which is treated
4720006a0bStron  *              as keyboard input after the command is executed.
4820006a0bStron  *
4920006a0bStron  *      Blank lines and lines which start with # are ignored,
5020006a0bStron  *      except for the special control lines:
5120006a0bStron  *              #command        Signals the beginning of the command
5220006a0bStron  *                              keys section.
5320006a0bStron  *              #line-edit      Signals the beginning of the line-editing
5420006a0bStron  *                              keys section.
5520006a0bStron  *              #env            Signals the beginning of the environment
5620006a0bStron  *                              variable section.
5720006a0bStron  *              #stop           Stops command parsing in less;
5820006a0bStron  *                              causes all default keys to be disabled.
5920006a0bStron  *
6020006a0bStron  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6120006a0bStron  *
6220006a0bStron  *      The output file is a non-ascii file, consisting of a header,
6320006a0bStron  *      one or more sections, and a trailer.
6420006a0bStron  *      Each section begins with a section header, a section length word
6520006a0bStron  *      and the section data.  Normally there are three sections:
6620006a0bStron  *              CMD_SECTION     Definition of command keys.
6720006a0bStron  *              EDIT_SECTION    Definition of editing keys.
6820006a0bStron  *              END_SECTION     A special section header, with no
6920006a0bStron  *                              length word or section data.
7020006a0bStron  *
7120006a0bStron  *      Section data consists of zero or more byte sequences of the form:
7220006a0bStron  *              string <0> <action>
7320006a0bStron  *      or
7420006a0bStron  *              string <0> <action|A_EXTRA> chars <0>
7520006a0bStron  *
7620006a0bStron  *      "string" is the command string.
7720006a0bStron  *      "<0>" is one null byte.
7820006a0bStron  *      "<action>" is one byte containing the action code (the A_xxx value).
7920006a0bStron  *      If action is ORed with A_EXTRA, the action byte is followed
8020006a0bStron  *              by the null-terminated "chars" string.
8120006a0bStron  *
8220006a0bStron  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8320006a0bStron  */
8420006a0bStron 
85838f5788Ssimonb #include "defines.h"
86838f5788Ssimonb #include <stdio.h>
87838f5788Ssimonb #include <string.h>
88838f5788Ssimonb #include <stdlib.h>
8920006a0bStron #include "lesskey.h"
9020006a0bStron #include "cmd.h"
9120006a0bStron 
9220006a0bStron char fileheader[] = {
9320006a0bStron 	C0_LESSKEY_MAGIC,
9420006a0bStron 	C1_LESSKEY_MAGIC,
9520006a0bStron 	C2_LESSKEY_MAGIC,
9620006a0bStron 	C3_LESSKEY_MAGIC
9720006a0bStron };
9820006a0bStron char filetrailer[] = {
9920006a0bStron 	C0_END_LESSKEY_MAGIC,
10020006a0bStron 	C1_END_LESSKEY_MAGIC,
10120006a0bStron 	C2_END_LESSKEY_MAGIC
10220006a0bStron };
10320006a0bStron char cmdsection[1] =    { CMD_SECTION };
10420006a0bStron char editsection[1] =   { EDIT_SECTION };
10520006a0bStron char varsection[1] =    { VAR_SECTION };
10620006a0bStron char endsection[1] =    { END_SECTION };
10720006a0bStron 
10820006a0bStron char *infile = NULL;
10920006a0bStron char *outfile = NULL ;
11020006a0bStron 
11120006a0bStron extern char version[];
11220006a0bStron 
usage(void)113838f5788Ssimonb static void usage(void)
11420006a0bStron {
11520006a0bStron 	fprintf(stderr, "usage: lesskey [-o output] [input]\n");
11620006a0bStron 	exit(1);
11720006a0bStron }
11820006a0bStron 
lesskey_parse_error(char * s)119838f5788Ssimonb void lesskey_parse_error(char *s)
120838f5788Ssimonb {
121838f5788Ssimonb 	fprintf(stderr, "%s\n", s);
122838f5788Ssimonb }
123838f5788Ssimonb 
lstrtoi(char * buf,char ** ebuf,int radix)124838f5788Ssimonb int lstrtoi(char *buf, char **ebuf, int radix)
125838f5788Ssimonb {
126838f5788Ssimonb 	return (int) strtol(buf, ebuf, radix);
127838f5788Ssimonb }
128838f5788Ssimonb 
out_of_memory(void)129838f5788Ssimonb void out_of_memory(void)
130838f5788Ssimonb {
131838f5788Ssimonb 	fprintf(stderr, "lesskey: cannot allocate memory\n");
132838f5788Ssimonb 	exit(1);
133838f5788Ssimonb }
134838f5788Ssimonb 
ecalloc(int count,unsigned int size)135838f5788Ssimonb void * ecalloc(int count, unsigned int size)
136838f5788Ssimonb {
137838f5788Ssimonb 	void *p;
138838f5788Ssimonb 
139838f5788Ssimonb 	p = calloc(count, size);
140838f5788Ssimonb 	if (p == NULL)
141838f5788Ssimonb 		out_of_memory();
142838f5788Ssimonb 	return (p);
143838f5788Ssimonb }
144838f5788Ssimonb 
mkpathname(char * dirname,char * filename)145838f5788Ssimonb static char * mkpathname(char *dirname, char *filename)
14620006a0bStron {
14720006a0bStron 	char *pathname;
14820006a0bStron 
149838f5788Ssimonb 	pathname = ecalloc(strlen(dirname) + strlen(filename) + 2, sizeof(char));
15020006a0bStron 	strcpy(pathname, dirname);
15120006a0bStron 	strcat(pathname, PATHNAME_SEP);
15220006a0bStron 	strcat(pathname, filename);
15320006a0bStron 	return (pathname);
15420006a0bStron }
15520006a0bStron 
15620006a0bStron /*
15720006a0bStron  * Figure out the name of a default file (in the user's HOME directory).
15820006a0bStron  */
homefile(char * filename)159838f5788Ssimonb char * homefile(char *filename)
16020006a0bStron {
16120006a0bStron 	char *p;
16220006a0bStron 	char *pathname;
16320006a0bStron 
16420006a0bStron 	if ((p = getenv("HOME")) != NULL && *p != '\0')
16520006a0bStron 		pathname = mkpathname(p, filename);
16620006a0bStron #if OS2
16720006a0bStron 	else if ((p = getenv("INIT")) != NULL && *p != '\0')
16820006a0bStron 		pathname = mkpathname(p, filename);
16920006a0bStron #endif
17020006a0bStron 	else
17120006a0bStron 	{
17220006a0bStron 		fprintf(stderr, "cannot find $HOME - using current directory\n");
17320006a0bStron 		pathname = mkpathname(".", filename);
17420006a0bStron 	}
17520006a0bStron 	return (pathname);
17620006a0bStron }
17720006a0bStron 
17820006a0bStron /*
17920006a0bStron  * Parse command line arguments.
18020006a0bStron  */
parse_args(int argc,char ** argv)181838f5788Ssimonb static void parse_args(int argc, char **argv)
18220006a0bStron {
18320006a0bStron 	char *arg;
18420006a0bStron 
18520006a0bStron 	outfile = NULL;
18620006a0bStron 	while (--argc > 0)
18720006a0bStron 	{
18820006a0bStron 		arg = *++argv;
18920006a0bStron 		if (arg[0] != '-')
19020006a0bStron 			/* Arg does not start with "-"; it's not an option. */
19120006a0bStron 			break;
19220006a0bStron 		if (arg[1] == '\0')
19320006a0bStron 			/* "-" means standard input. */
19420006a0bStron 			break;
19520006a0bStron 		if (arg[1] == '-' && arg[2] == '\0')
19620006a0bStron 		{
19720006a0bStron 			/* "--" means end of options. */
19820006a0bStron 			argc--;
19920006a0bStron 			argv++;
20020006a0bStron 			break;
20120006a0bStron 		}
20220006a0bStron 		switch (arg[1])
20320006a0bStron 		{
20420006a0bStron 		case '-':
20520006a0bStron 			if (strncmp(arg, "--output", 8) == 0)
20620006a0bStron 			{
20720006a0bStron 				if (arg[8] == '\0')
20820006a0bStron 					outfile = &arg[8];
20920006a0bStron 				else if (arg[8] == '=')
21020006a0bStron 					outfile = &arg[9];
21120006a0bStron 				else
21220006a0bStron 					usage();
21320006a0bStron 				goto opt_o;
21420006a0bStron 			}
21520006a0bStron 			if (strcmp(arg, "--version") == 0)
21620006a0bStron 			{
21720006a0bStron 				goto opt_V;
21820006a0bStron 			}
21920006a0bStron 			usage();
22020006a0bStron 			break;
22120006a0bStron 		case 'o':
22220006a0bStron 			outfile = &argv[0][2];
22320006a0bStron 		opt_o:
22420006a0bStron 			if (*outfile == '\0')
22520006a0bStron 			{
22620006a0bStron 				if (--argc <= 0)
22720006a0bStron 					usage();
22820006a0bStron 				outfile = *(++argv);
22920006a0bStron 			}
23020006a0bStron 			break;
23120006a0bStron 		case 'V':
23220006a0bStron 		opt_V:
23320006a0bStron 			printf("lesskey  version %s\n", version);
23420006a0bStron 			exit(0);
23520006a0bStron 		default:
23620006a0bStron 			usage();
23720006a0bStron 		}
23820006a0bStron 	}
23920006a0bStron 	if (argc > 1)
24020006a0bStron 		usage();
24120006a0bStron 	/*
24220006a0bStron 	 * Open the input file, or use DEF_LESSKEYINFILE if none specified.
24320006a0bStron 	 */
24420006a0bStron 	if (argc > 0)
24520006a0bStron 		infile = *argv;
24620006a0bStron }
24720006a0bStron 
24820006a0bStron /*
24920006a0bStron  * Output some bytes.
25020006a0bStron  */
fputbytes(FILE * fd,char * buf,int len)251838f5788Ssimonb static void fputbytes(FILE *fd, char *buf, int len)
25220006a0bStron {
25320006a0bStron 	while (len-- > 0)
25420006a0bStron 	{
25520006a0bStron 		fwrite(buf, sizeof(char), 1, fd);
25620006a0bStron 		buf++;
25720006a0bStron 	}
25820006a0bStron }
25920006a0bStron 
26020006a0bStron /*
26120006a0bStron  * Output an integer, in special KRADIX form.
26220006a0bStron  */
fputint(FILE * fd,unsigned int val)263838f5788Ssimonb static void fputint(FILE *fd, unsigned int val)
26420006a0bStron {
26520006a0bStron 	char c;
26620006a0bStron 
26720006a0bStron 	if (val >= KRADIX*KRADIX)
26820006a0bStron 	{
269838f5788Ssimonb 		fprintf(stderr, "error: cannot write %d, max %d\n",
27020006a0bStron 			val, KRADIX*KRADIX);
27120006a0bStron 		exit(1);
27220006a0bStron 	}
27320006a0bStron 	c = val % KRADIX;
27420006a0bStron 	fwrite(&c, sizeof(char), 1, fd);
27520006a0bStron 	c = val / KRADIX;
27620006a0bStron 	fwrite(&c, sizeof(char), 1, fd);
27720006a0bStron }
27820006a0bStron 
main(int argc,char * argv[])279838f5788Ssimonb int main(int argc, char *argv[])
28020006a0bStron {
281838f5788Ssimonb 	struct lesskey_tables tables;
28220006a0bStron 	FILE *out;
283838f5788Ssimonb 	int errors;
28420006a0bStron 
28520006a0bStron #ifdef WIN32
28620006a0bStron 	if (getenv("HOME") == NULL)
28720006a0bStron 	{
28820006a0bStron 		/*
28920006a0bStron 		 * If there is no HOME environment variable,
29020006a0bStron 		 * try the concatenation of HOMEDRIVE + HOMEPATH.
29120006a0bStron 		 */
29220006a0bStron 		char *drive = getenv("HOMEDRIVE");
29320006a0bStron 		char *path  = getenv("HOMEPATH");
29420006a0bStron 		if (drive != NULL && path != NULL)
29520006a0bStron 		{
296838f5788Ssimonb 			char *env = (char *) ecalloc(strlen(drive) +
29720006a0bStron 					strlen(path) + 6, sizeof(char));
29820006a0bStron 			strcpy(env, "HOME=");
29920006a0bStron 			strcat(env, drive);
30020006a0bStron 			strcat(env, path);
30120006a0bStron 			putenv(env);
30220006a0bStron 		}
30320006a0bStron 	}
30420006a0bStron #endif /* WIN32 */
30520006a0bStron 
30620006a0bStron 	/*
30720006a0bStron 	 * Process command line arguments.
30820006a0bStron 	 */
30920006a0bStron 	parse_args(argc, argv);
310838f5788Ssimonb 	errors = parse_lesskey(infile, &tables);
311838f5788Ssimonb 	if (errors)
31220006a0bStron 	{
313838f5788Ssimonb 		fprintf(stderr, "%d errors; no output produced\n", errors);
314838f5788Ssimonb 		return (1);
31520006a0bStron 	}
31620006a0bStron 
317838f5788Ssimonb 	fprintf(stderr, "NOTE: lesskey is deprecated.\n      It is no longer necessary to run lesskey,\n      when using less version 582 and later.\n");
31820006a0bStron 
31920006a0bStron 	/*
32020006a0bStron 	 * Write the output file.
32120006a0bStron 	 * If no output file was specified, use "$HOME/.less"
32220006a0bStron 	 */
32320006a0bStron 	if (outfile == NULL)
32420006a0bStron 		outfile = getenv("LESSKEY");
32520006a0bStron 	if (outfile == NULL)
32620006a0bStron 		outfile = homefile(LESSKEYFILE);
32720006a0bStron 	if ((out = fopen(outfile, "wb")) == NULL)
32820006a0bStron 	{
32920006a0bStron #if HAVE_PERROR
33020006a0bStron 		perror(outfile);
33120006a0bStron #else
33220006a0bStron 		fprintf(stderr, "Cannot open %s\n", outfile);
33320006a0bStron #endif
334838f5788Ssimonb 		return (1);
33520006a0bStron 	}
33620006a0bStron 
33720006a0bStron 	/* File header */
33820006a0bStron 	fputbytes(out, fileheader, sizeof(fileheader));
33920006a0bStron 
34020006a0bStron 	/* Command key section */
34120006a0bStron 	fputbytes(out, cmdsection, sizeof(cmdsection));
342838f5788Ssimonb 	fputint(out, tables.cmdtable.buf.end);
343838f5788Ssimonb 	fputbytes(out, (char *)tables.cmdtable.buf.data, tables.cmdtable.buf.end);
34420006a0bStron 	/* Edit key section */
34520006a0bStron 	fputbytes(out, editsection, sizeof(editsection));
346838f5788Ssimonb 	fputint(out, tables.edittable.buf.end);
347838f5788Ssimonb 	fputbytes(out, (char *)tables.edittable.buf.data, tables.edittable.buf.end);
34820006a0bStron 
34920006a0bStron 	/* Environment variable section */
35020006a0bStron 	fputbytes(out, varsection, sizeof(varsection));
351838f5788Ssimonb 	fputint(out, tables.vartable.buf.end);
352838f5788Ssimonb 	fputbytes(out, (char *)tables.vartable.buf.data, tables.vartable.buf.end);
35320006a0bStron 
35420006a0bStron 	/* File trailer */
35520006a0bStron 	fputbytes(out, endsection, sizeof(endsection));
35620006a0bStron 	fputbytes(out, filetrailer, sizeof(filetrailer));
357838f5788Ssimonb 	fclose(out);
35820006a0bStron 	return (0);
35920006a0bStron }
360