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