xref: /freebsd/contrib/less/lesskey.c (revision c77c488926555ca344ae3a417544cf7a720e1de1)
1a5f0fb15SPaul Saab /*
2*c77c4889SXin LI  * Copyright (C) 1984-2024  Mark Nudelman
3a5f0fb15SPaul Saab  *
4a5f0fb15SPaul Saab  * You may distribute under the terms of either the GNU General Public
5a5f0fb15SPaul Saab  * License or the Less License, as specified in the README file.
6a5f0fb15SPaul Saab  *
796e55cc7SXin LI  * For more information, see the README file.
8a5f0fb15SPaul Saab  */
9a5f0fb15SPaul Saab 
10a5f0fb15SPaul Saab 
11a5f0fb15SPaul Saab /*
12a5f0fb15SPaul Saab  *  lesskey [-o output] [input]
13a5f0fb15SPaul Saab  *
14a5f0fb15SPaul Saab  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15a5f0fb15SPaul Saab  *
16a5f0fb15SPaul Saab  *  Make a .less file.
17a5f0fb15SPaul Saab  *  If no input file is specified, standard input is used.
18a5f0fb15SPaul Saab  *  If no output file is specified, $HOME/.less is used.
19a5f0fb15SPaul Saab  *
20a5f0fb15SPaul Saab  *  The .less file is used to specify (to "less") user-defined
21a5f0fb15SPaul Saab  *  key bindings.  Basically any sequence of 1 to MAX_CMDLEN
22a5f0fb15SPaul Saab  *  keystrokes may be bound to an existing less function.
23a5f0fb15SPaul Saab  *
24a5f0fb15SPaul Saab  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
25a5f0fb15SPaul Saab  *
26a5f0fb15SPaul Saab  *  The input file is an ascii file consisting of a
27a5f0fb15SPaul Saab  *  sequence of lines of the form:
28a5f0fb15SPaul Saab  *          string <whitespace> action [chars] <newline>
29a5f0fb15SPaul Saab  *
30a5f0fb15SPaul Saab  *      "string" is a sequence of command characters which form
31a5f0fb15SPaul Saab  *              the new user-defined command.  The command
32a5f0fb15SPaul Saab  *              characters may be:
33a5f0fb15SPaul Saab  *              1. The actual character itself.
34a5f0fb15SPaul Saab  *              2. A character preceded by ^ to specify a
35a5f0fb15SPaul Saab  *                 control character (e.g. ^X means control-X).
36a5f0fb15SPaul Saab  *              3. A backslash followed by one to three octal digits
37a5f0fb15SPaul Saab  *                 to specify a character by its octal value.
38a5f0fb15SPaul Saab  *              4. A backslash followed by b, e, n, r or t
39a5f0fb15SPaul Saab  *                 to specify \b, ESC, \n, \r or \t, respectively.
40a5f0fb15SPaul Saab  *              5. Any character (other than those mentioned above) preceded
41a5f0fb15SPaul Saab  *                 by a \ to specify the character itself (characters which
42a5f0fb15SPaul Saab  *                 must be preceded by \ include ^, \, and whitespace.
43a5f0fb15SPaul Saab  *      "action" is the name of a "less" action, from the table below.
44a5f0fb15SPaul Saab  *      "chars" is an optional sequence of characters which is treated
45a5f0fb15SPaul Saab  *              as keyboard input after the command is executed.
46a5f0fb15SPaul Saab  *
47a5f0fb15SPaul Saab  *      Blank lines and lines which start with # are ignored,
48a5f0fb15SPaul Saab  *      except for the special control lines:
49a5f0fb15SPaul Saab  *              #command        Signals the beginning of the command
50a5f0fb15SPaul Saab  *                              keys section.
51a5f0fb15SPaul Saab  *              #line-edit      Signals the beginning of the line-editing
52a5f0fb15SPaul Saab  *                              keys section.
53a5f0fb15SPaul Saab  *              #env            Signals the beginning of the environment
54a5f0fb15SPaul Saab  *                              variable section.
55a5f0fb15SPaul Saab  *              #stop           Stops command parsing in less;
56a5f0fb15SPaul Saab  *                              causes all default keys to be disabled.
57a5f0fb15SPaul Saab  *
58a5f0fb15SPaul Saab  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
59a5f0fb15SPaul Saab  *
60a5f0fb15SPaul Saab  *      The output file is a non-ascii file, consisting of a header,
61a5f0fb15SPaul Saab  *      one or more sections, and a trailer.
62a5f0fb15SPaul Saab  *      Each section begins with a section header, a section length word
63a5f0fb15SPaul Saab  *      and the section data.  Normally there are three sections:
64a5f0fb15SPaul Saab  *              CMD_SECTION     Definition of command keys.
65a5f0fb15SPaul Saab  *              EDIT_SECTION    Definition of editing keys.
66a5f0fb15SPaul Saab  *              END_SECTION     A special section header, with no
67a5f0fb15SPaul Saab  *                              length word or section data.
68a5f0fb15SPaul Saab  *
69a5f0fb15SPaul Saab  *      Section data consists of zero or more byte sequences of the form:
70a5f0fb15SPaul Saab  *              string <0> <action>
71a5f0fb15SPaul Saab  *      or
72a5f0fb15SPaul Saab  *              string <0> <action|A_EXTRA> chars <0>
73a5f0fb15SPaul Saab  *
74a5f0fb15SPaul Saab  *      "string" is the command string.
75a5f0fb15SPaul Saab  *      "<0>" is one null byte.
76a5f0fb15SPaul Saab  *      "<action>" is one byte containing the action code (the A_xxx value).
77a5f0fb15SPaul Saab  *      If action is ORed with A_EXTRA, the action byte is followed
78a5f0fb15SPaul Saab  *              by the null-terminated "chars" string.
79a5f0fb15SPaul Saab  *
80a5f0fb15SPaul Saab  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
81a5f0fb15SPaul Saab  */
82a5f0fb15SPaul Saab 
83d713e089SXin LI #include "defines.h"
8430a1828cSXin LI #include <stdio.h>
8530a1828cSXin LI #include <string.h>
8630a1828cSXin LI #include <stdlib.h>
87a5f0fb15SPaul Saab #include "lesskey.h"
88a5f0fb15SPaul Saab #include "cmd.h"
89a5f0fb15SPaul Saab 
90*c77c4889SXin LI constant char fileheader[] = {
91a5f0fb15SPaul Saab 	C0_LESSKEY_MAGIC,
92a5f0fb15SPaul Saab 	C1_LESSKEY_MAGIC,
93a5f0fb15SPaul Saab 	C2_LESSKEY_MAGIC,
94a5f0fb15SPaul Saab 	C3_LESSKEY_MAGIC
95a5f0fb15SPaul Saab };
96*c77c4889SXin LI constant char filetrailer[] = {
97a5f0fb15SPaul Saab 	C0_END_LESSKEY_MAGIC,
98a5f0fb15SPaul Saab 	C1_END_LESSKEY_MAGIC,
99a5f0fb15SPaul Saab 	C2_END_LESSKEY_MAGIC
100a5f0fb15SPaul Saab };
101*c77c4889SXin LI constant char cmdsection[1] =    { CMD_SECTION };
102*c77c4889SXin LI constant char editsection[1] =   { EDIT_SECTION };
103*c77c4889SXin LI constant char varsection[1] =    { VAR_SECTION };
104*c77c4889SXin LI constant char endsection[1] =    { END_SECTION };
105a5f0fb15SPaul Saab 
106*c77c4889SXin LI constant char *infile = NULL;
107*c77c4889SXin LI constant char *outfile = NULL;
108a5f0fb15SPaul Saab 
109a5f0fb15SPaul Saab extern char version[];
110a5f0fb15SPaul Saab 
111d713e089SXin LI static void usage(void)
112a5f0fb15SPaul Saab {
113a5f0fb15SPaul Saab 	fprintf(stderr, "usage: lesskey [-o output] [input]\n");
114a5f0fb15SPaul Saab 	exit(1);
115a5f0fb15SPaul Saab }
116a5f0fb15SPaul Saab 
117*c77c4889SXin LI void lesskey_parse_error(constant char *s)
11830a1828cSXin LI {
11930a1828cSXin LI 	fprintf(stderr, "%s\n", s);
12030a1828cSXin LI }
12130a1828cSXin LI 
122*c77c4889SXin LI int lstrtoi(constant char *buf, constant char **ebuf, int radix)
12395270f73SXin LI {
124*c77c4889SXin LI 	return (int) strtol(buf, (char**)ebuf, radix);
12595270f73SXin LI }
12695270f73SXin LI 
127d713e089SXin LI void out_of_memory(void)
128d713e089SXin LI {
129d713e089SXin LI 	fprintf(stderr, "lesskey: cannot allocate memory\n");
130d713e089SXin LI 	exit(1);
131d713e089SXin LI }
132d713e089SXin LI 
133*c77c4889SXin LI void * ecalloc(size_t count, size_t size)
13430a1828cSXin LI {
13530a1828cSXin LI 	void *p;
13630a1828cSXin LI 
13730a1828cSXin LI 	p = calloc(count, size);
138d713e089SXin LI 	if (p == NULL)
139d713e089SXin LI 		out_of_memory();
14030a1828cSXin LI 	return (p);
14130a1828cSXin LI }
14230a1828cSXin LI 
143*c77c4889SXin LI static char * mkpathname(constant char *dirname, constant char *filename)
144a5f0fb15SPaul Saab {
145a5f0fb15SPaul Saab 	char *pathname;
146a5f0fb15SPaul Saab 
14730a1828cSXin LI 	pathname = ecalloc(strlen(dirname) + strlen(filename) + 2, sizeof(char));
148a5f0fb15SPaul Saab 	strcpy(pathname, dirname);
149a5f0fb15SPaul Saab 	strcat(pathname, PATHNAME_SEP);
150a5f0fb15SPaul Saab 	strcat(pathname, filename);
151a5f0fb15SPaul Saab 	return (pathname);
152a5f0fb15SPaul Saab }
153a5f0fb15SPaul Saab 
154a5f0fb15SPaul Saab /*
155a5f0fb15SPaul Saab  * Figure out the name of a default file (in the user's HOME directory).
156a5f0fb15SPaul Saab  */
157*c77c4889SXin LI char * homefile(constant char *filename)
158a5f0fb15SPaul Saab {
159*c77c4889SXin LI 	constant char *p;
160a5f0fb15SPaul Saab 	char *pathname;
161a5f0fb15SPaul Saab 
162a5f0fb15SPaul Saab 	if ((p = getenv("HOME")) != NULL && *p != '\0')
163a5f0fb15SPaul Saab 		pathname = mkpathname(p, filename);
164a5f0fb15SPaul Saab #if OS2
165a5f0fb15SPaul Saab 	else if ((p = getenv("INIT")) != NULL && *p != '\0')
166a5f0fb15SPaul Saab 		pathname = mkpathname(p, filename);
167a5f0fb15SPaul Saab #endif
168a5f0fb15SPaul Saab 	else
169a5f0fb15SPaul Saab 	{
170a5f0fb15SPaul Saab 		fprintf(stderr, "cannot find $HOME - using current directory\n");
171a5f0fb15SPaul Saab 		pathname = mkpathname(".", filename);
172a5f0fb15SPaul Saab 	}
173a5f0fb15SPaul Saab 	return (pathname);
174a5f0fb15SPaul Saab }
175a5f0fb15SPaul Saab 
176a5f0fb15SPaul Saab /*
177a5f0fb15SPaul Saab  * Parse command line arguments.
178a5f0fb15SPaul Saab  */
179*c77c4889SXin LI static void parse_args(int argc, constant char **argv)
180a5f0fb15SPaul Saab {
181*c77c4889SXin LI 	constant char *arg;
182a5f0fb15SPaul Saab 
183a5f0fb15SPaul Saab 	outfile = NULL;
184a5f0fb15SPaul Saab 	while (--argc > 0)
185a5f0fb15SPaul Saab 	{
186a5f0fb15SPaul Saab 		arg = *++argv;
187a5f0fb15SPaul Saab 		if (arg[0] != '-')
188a5f0fb15SPaul Saab 			/* Arg does not start with "-"; it's not an option. */
189a5f0fb15SPaul Saab 			break;
190a5f0fb15SPaul Saab 		if (arg[1] == '\0')
191a5f0fb15SPaul Saab 			/* "-" means standard input. */
192a5f0fb15SPaul Saab 			break;
193a5f0fb15SPaul Saab 		if (arg[1] == '-' && arg[2] == '\0')
194a5f0fb15SPaul Saab 		{
195a5f0fb15SPaul Saab 			/* "--" means end of options. */
196a5f0fb15SPaul Saab 			argc--;
197a5f0fb15SPaul Saab 			argv++;
198a5f0fb15SPaul Saab 			break;
199a5f0fb15SPaul Saab 		}
200a5f0fb15SPaul Saab 		switch (arg[1])
201a5f0fb15SPaul Saab 		{
202a5f0fb15SPaul Saab 		case '-':
203a5f0fb15SPaul Saab 			if (strncmp(arg, "--output", 8) == 0)
204a5f0fb15SPaul Saab 			{
205a5f0fb15SPaul Saab 				if (arg[8] == '\0')
206a5f0fb15SPaul Saab 					outfile = &arg[8];
207a5f0fb15SPaul Saab 				else if (arg[8] == '=')
208a5f0fb15SPaul Saab 					outfile = &arg[9];
209a5f0fb15SPaul Saab 				else
210a5f0fb15SPaul Saab 					usage();
211a5f0fb15SPaul Saab 				goto opt_o;
212a5f0fb15SPaul Saab 			}
213a5f0fb15SPaul Saab 			if (strcmp(arg, "--version") == 0)
214a5f0fb15SPaul Saab 			{
215a5f0fb15SPaul Saab 				goto opt_V;
216a5f0fb15SPaul Saab 			}
217a5f0fb15SPaul Saab 			usage();
218a5f0fb15SPaul Saab 			break;
219a5f0fb15SPaul Saab 		case 'o':
220a5f0fb15SPaul Saab 			outfile = &argv[0][2];
221a5f0fb15SPaul Saab 		opt_o:
222a5f0fb15SPaul Saab 			if (*outfile == '\0')
223a5f0fb15SPaul Saab 			{
224a5f0fb15SPaul Saab 				if (--argc <= 0)
225a5f0fb15SPaul Saab 					usage();
226a5f0fb15SPaul Saab 				outfile = *(++argv);
227a5f0fb15SPaul Saab 			}
228a5f0fb15SPaul Saab 			break;
229a5f0fb15SPaul Saab 		case 'V':
230a5f0fb15SPaul Saab 		opt_V:
231a5f0fb15SPaul Saab 			printf("lesskey  version %s\n", version);
232a5f0fb15SPaul Saab 			exit(0);
233a5f0fb15SPaul Saab 		default:
234a5f0fb15SPaul Saab 			usage();
235a5f0fb15SPaul Saab 		}
236a5f0fb15SPaul Saab 	}
237a5f0fb15SPaul Saab 	if (argc > 1)
238a5f0fb15SPaul Saab 		usage();
239a5f0fb15SPaul Saab 	/*
240a5f0fb15SPaul Saab 	 * Open the input file, or use DEF_LESSKEYINFILE if none specified.
241a5f0fb15SPaul Saab 	 */
242a5f0fb15SPaul Saab 	if (argc > 0)
243a5f0fb15SPaul Saab 		infile = *argv;
244a5f0fb15SPaul Saab }
245a5f0fb15SPaul Saab 
246a5f0fb15SPaul Saab /*
247a5f0fb15SPaul Saab  * Output some bytes.
248a5f0fb15SPaul Saab  */
249*c77c4889SXin LI static void fputbytes(FILE *fd, constant char *buf, size_t len)
250a5f0fb15SPaul Saab {
251a5f0fb15SPaul Saab 	while (len-- > 0)
252a5f0fb15SPaul Saab 	{
253a5f0fb15SPaul Saab 		fwrite(buf, sizeof(char), 1, fd);
254a5f0fb15SPaul Saab 		buf++;
255a5f0fb15SPaul Saab 	}
256a5f0fb15SPaul Saab }
257a5f0fb15SPaul Saab 
258a5f0fb15SPaul Saab /*
259a5f0fb15SPaul Saab  * Output an integer, in special KRADIX form.
260a5f0fb15SPaul Saab  */
261*c77c4889SXin LI static void fputint(FILE *fd, size_t val)
262a5f0fb15SPaul Saab {
263*c77c4889SXin LI 	char c1, c2;
264a5f0fb15SPaul Saab 
265a5f0fb15SPaul Saab 	if (val >= KRADIX*KRADIX)
266a5f0fb15SPaul Saab 	{
267*c77c4889SXin LI 		fprintf(stderr, "error: cannot write %ld, max %ld\n",
268*c77c4889SXin LI 			(long) val, (long) (KRADIX*KRADIX));
269a5f0fb15SPaul Saab 		exit(1);
270a5f0fb15SPaul Saab 	}
271*c77c4889SXin LI 	c1 = (char) (val % KRADIX);
272*c77c4889SXin LI 	val /= KRADIX;
273*c77c4889SXin LI 	c2 = (char) (val % KRADIX);
274*c77c4889SXin LI 	val /= KRADIX;
275*c77c4889SXin LI 	if (val != 0) {
276*c77c4889SXin LI 		fprintf(stderr, "error: %ld exceeds max integer size (%ld)\n",
277*c77c4889SXin LI 			(long) val, (long) (KRADIX*KRADIX));
278*c77c4889SXin LI 		exit(1);
279*c77c4889SXin LI 	}
280*c77c4889SXin LI 	fwrite(&c1, sizeof(char), 1, fd);
281*c77c4889SXin LI 	fwrite(&c2, sizeof(char), 1, fd);
282a5f0fb15SPaul Saab }
283a5f0fb15SPaul Saab 
284*c77c4889SXin LI int main(int argc, constant char *argv[])
285a5f0fb15SPaul Saab {
28630a1828cSXin LI 	struct lesskey_tables tables;
287a5f0fb15SPaul Saab 	FILE *out;
28830a1828cSXin LI 	int errors;
289a5f0fb15SPaul Saab 
290a5f0fb15SPaul Saab #ifdef WIN32
291a5f0fb15SPaul Saab 	if (getenv("HOME") == NULL)
292a5f0fb15SPaul Saab 	{
293a5f0fb15SPaul Saab 		/*
294a5f0fb15SPaul Saab 		 * If there is no HOME environment variable,
295a5f0fb15SPaul Saab 		 * try the concatenation of HOMEDRIVE + HOMEPATH.
296a5f0fb15SPaul Saab 		 */
297*c77c4889SXin LI 		constant char *drive = getenv("HOMEDRIVE");
298*c77c4889SXin LI 		constant char *path  = getenv("HOMEPATH");
299a5f0fb15SPaul Saab 		if (drive != NULL && path != NULL)
300a5f0fb15SPaul Saab 		{
30130a1828cSXin LI 			char *env = (char *) ecalloc(strlen(drive) +
302a5f0fb15SPaul Saab 					strlen(path) + 6, sizeof(char));
303a5f0fb15SPaul Saab 			strcpy(env, "HOME=");
304a5f0fb15SPaul Saab 			strcat(env, drive);
305a5f0fb15SPaul Saab 			strcat(env, path);
306a5f0fb15SPaul Saab 			putenv(env);
307a5f0fb15SPaul Saab 		}
308a5f0fb15SPaul Saab 	}
309a5f0fb15SPaul Saab #endif /* WIN32 */
310a5f0fb15SPaul Saab 
311a5f0fb15SPaul Saab 	/*
312a5f0fb15SPaul Saab 	 * Process command line arguments.
313a5f0fb15SPaul Saab 	 */
314a5f0fb15SPaul Saab 	parse_args(argc, argv);
31530a1828cSXin LI 	errors = parse_lesskey(infile, &tables);
31630a1828cSXin LI 	if (errors)
317a5f0fb15SPaul Saab 	{
31830a1828cSXin LI 		fprintf(stderr, "%d errors; no output produced\n", errors);
31930a1828cSXin LI 		return (1);
320a5f0fb15SPaul Saab 	}
321a5f0fb15SPaul Saab 
32230a1828cSXin LI 	fprintf(stderr, "NOTE: lesskey is deprecated.\n      It is no longer necessary to run lesskey,\n      when using less version 582 and later.\n");
323a5f0fb15SPaul Saab 
324a5f0fb15SPaul Saab 	/*
325a5f0fb15SPaul Saab 	 * Write the output file.
326a5f0fb15SPaul Saab 	 * If no output file was specified, use "$HOME/.less"
327a5f0fb15SPaul Saab 	 */
328a5f0fb15SPaul Saab 	if (outfile == NULL)
329a5f0fb15SPaul Saab 		outfile = getenv("LESSKEY");
330a5f0fb15SPaul Saab 	if (outfile == NULL)
331a5f0fb15SPaul Saab 		outfile = homefile(LESSKEYFILE);
332a5f0fb15SPaul Saab 	if ((out = fopen(outfile, "wb")) == NULL)
333a5f0fb15SPaul Saab 	{
334a5f0fb15SPaul Saab #if HAVE_PERROR
335a5f0fb15SPaul Saab 		perror(outfile);
336a5f0fb15SPaul Saab #else
337a5f0fb15SPaul Saab 		fprintf(stderr, "Cannot open %s\n", outfile);
338a5f0fb15SPaul Saab #endif
33930a1828cSXin LI 		return (1);
340a5f0fb15SPaul Saab 	}
341a5f0fb15SPaul Saab 
342a5f0fb15SPaul Saab 	/* File header */
343a5f0fb15SPaul Saab 	fputbytes(out, fileheader, sizeof(fileheader));
344a5f0fb15SPaul Saab 
345a5f0fb15SPaul Saab 	/* Command key section */
346a5f0fb15SPaul Saab 	fputbytes(out, cmdsection, sizeof(cmdsection));
34730a1828cSXin LI 	fputint(out, tables.cmdtable.buf.end);
348*c77c4889SXin LI 	fputbytes(out, xbuf_char_data(&tables.cmdtable.buf), tables.cmdtable.buf.end);
349a5f0fb15SPaul Saab 	/* Edit key section */
350a5f0fb15SPaul Saab 	fputbytes(out, editsection, sizeof(editsection));
35130a1828cSXin LI 	fputint(out, tables.edittable.buf.end);
352*c77c4889SXin LI 	fputbytes(out, xbuf_char_data(&tables.edittable.buf), tables.edittable.buf.end);
353a5f0fb15SPaul Saab 
354a5f0fb15SPaul Saab 	/* Environment variable section */
355a5f0fb15SPaul Saab 	fputbytes(out, varsection, sizeof(varsection));
35630a1828cSXin LI 	fputint(out, tables.vartable.buf.end);
357*c77c4889SXin LI 	fputbytes(out, xbuf_char_data(&tables.vartable.buf), tables.vartable.buf.end);
358a5f0fb15SPaul Saab 
359a5f0fb15SPaul Saab 	/* File trailer */
360a5f0fb15SPaul Saab 	fputbytes(out, endsection, sizeof(endsection));
361a5f0fb15SPaul Saab 	fputbytes(out, filetrailer, sizeof(filetrailer));
36295270f73SXin LI 	fclose(out);
363a5f0fb15SPaul Saab 	return (0);
364a5f0fb15SPaul Saab }
365