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