xref: /original-bsd/usr.bin/more/lesskey.c (revision 94cb6cb2)
1 /*
2  * Copyright (c) 1988 Mark Nudleman
3  * Copyright (c) 1988 Regents of the University of California.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms are permitted
7  * provided that the above copyright notice and this paragraph are
8  * duplicated in all such forms and that any documentation,
9  * advertising materials, and other materials related to such
10  * distribution and use acknowledge that the software was developed
11  * by Mark Nudleman and the University of California, Berkeley.  The
12  * name of Mark Nudleman or the
13  * University may not be used to endorse or promote products derived
14  * from this software without specific prior written permission.
15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18  */
19 
20 #ifndef lint
21 char copyright[] =
22 "@(#) Copyright (c) 1988 Mark Nudleman.\n\
23 @(#) Copyright (c) 1988 Regents of the University of California.\n\
24  All rights reserved.\n";
25 #endif /* not lint */
26 
27 #ifndef lint
28 static char sccsid[] = "@(#)lesskey.c	5.3 (Berkeley) 07/25/88";
29 #endif /* not lint */
30 
31 /*
32  *	lesskey [-o output] [input]
33  *
34  *	Make a .less file.
35  *	If no input file is specified, standard input is used.
36  *	If no output file is specified, $HOME/.less is used.
37  *
38  *	The .less file is used to specify (to "less") user-defined
39  *	key bindings.  Basically any sequence of 1 to MAX_CMDLEN
40  *	keystrokes may be bound to an existing less function.
41  *
42  *	The input file is an ascii file consisting of a
43  *	sequence of lines of the form:
44  *		string <whitespace> action <newline>
45  *
46  *	"string" is a sequence of command characters which form
47  *		the new user-defined command.  The command
48  *		characters may be:
49  *		1. The actual character itself.
50  *		2. A character preceeded by ^ to specify a
51  *		   control character (e.g. ^X means control-X).
52  *		3. Any character (other than an octal digit) preceeded by
53  *		   a \ to specify the character itself (characters which
54  *		   must be preceeded by \ include ^, \, and whitespace.
55  *		4. A backslash followed by one to three octal digits
56  *		   to specify a character by its octal value.
57  *	"action" is the name of a "less" action, from the table below.
58  *
59  *	Blank lines and lines which start with # are ignored.
60  *
61  *
62  *	The output file is a non-ascii file, consisting of
63  *	zero or more byte sequences of the form:
64  *		string <0> <action>
65  *
66  *	"string" is the command string.
67  *	"<0>" is one null byte.
68  *	"<action>" is one byte containing the action code (the A_xxx value).
69  *
70  *
71  *		Revision history
72  *
73  *	v1: Initial version.					10/13/87  mark
74  */
75 
76 #include <stdio.h>
77 #include "less.h"
78 #include "cmd.h"
79 
80 char usertable[MAX_USERCMD];
81 
82 struct cmdname
83 {
84 	char *cn_name;
85 	int cn_action;
86 } cmdnames[] =
87 {
88 	"back-line",		A_B_LINE,
89 	"back-screen",		A_B_SCREEN,
90 	"back-scroll",		A_B_SCROLL,
91 	"back-search",		A_B_SEARCH,
92 	"debug",		A_DEBUG,
93 	"display-flag",		A_DISP_OPTION,
94 	"display-option",	A_DISP_OPTION,
95 	"end",			A_GOEND,
96 	"examine",		A_EXAMINE,
97 	"first-cmd",		A_FIRSTCMD,
98 	"firstcmd",		A_FIRSTCMD,
99 	"flush-repaint",	A_FREPAINT,
100 	"forw-line",		A_F_LINE,
101 	"forw-screen",		A_F_SCREEN,
102 	"forw-scroll",		A_F_SCROLL,
103 	"forw-search",		A_F_SEARCH,
104 	"goto-end",		A_GOEND,
105 	"goto-line",		A_GOLINE,
106 	"goto-mark",		A_GOMARK,
107 	"help",			A_HELP,
108 	"invalid",		A_NOACTION,
109 	"next-file",		A_NEXT_FILE,
110 	"noaction",		A_NOACTION,
111 	"percent",		A_PERCENT,
112 	"prev-file",		A_PREV_FILE,
113 	"quit",			A_QUIT,
114 	"repaint",		A_REPAINT,
115 	"repaint-flush",	A_FREPAINT,
116 	"repeat-search",	A_AGAIN_SEARCH,
117 	"set-mark",		A_SETMARK,
118 	"shell",		A_SHELL,
119 	"status",		A_STAT,
120 	"toggle-flag",		A_TOGGLE_OPTION,
121 	"toggle-option",	A_TOGGLE_OPTION,
122 	"version",		A_VERSION,
123 	"visual",		A_VISUAL,
124 	NULL,			0
125 };
126 
main(argc,argv)127 main(argc, argv)
128 	int argc;
129 	char *argv[];
130 {
131 	char *p;		/* {{ Can't be register since we use &p }} */
132 	register char *up;	/* Pointer into usertable */
133 	FILE *desc;		/* Description file (input) */
134 	FILE *out;		/* Output file */
135 	int linenum;		/* Line number in input file */
136 	char *currcmd;		/* Start of current command string */
137 	int errors;
138 	int i;
139 	char line[100];
140 	char *outfile;
141 
142 	extern char *getenv(), *strcat(), *strcpy();
143 
144 	/*
145 	 * Process command line arguments.
146 	 */
147 	outfile = NULL;
148 	while (--argc > 0 && **(++argv) == '-')
149 	{
150 		switch (argv[0][1])
151 		{
152 		case 'o':
153 			outfile = &argv[0][2];
154 			if (*outfile == '\0')
155 			{
156 				if (--argc <= 0)
157 					usage();
158 				outfile = *(++argv);
159 			}
160 			break;
161 		default:
162 			usage();
163 		}
164 	}
165 	if (argc > 1)
166 		usage();
167 
168 
169 	/*
170 	 * Open the input file, or use standard input if none specified.
171 	 */
172 	if (argc > 0)
173 	{
174 		if ((desc = fopen(*argv, "r")) == NULL)
175 		{
176 			perror(*argv);
177 			exit(1);
178 		}
179 	} else
180 		desc = stdin;
181 
182 	/*
183 	 * Read the input file, one line at a time.
184 	 * Each line consists of a command string,
185 	 * followed by white space, followed by an action name.
186 	 */
187 	linenum = 0;
188 	errors = 0;
189 	up = usertable;
190 	while (fgets(line, sizeof(line), desc) != NULL)
191 	{
192 		++linenum;
193 
194 		/*
195 		 * Skip leading white space.
196 		 * Replace the final newline with a null byte.
197 		 * Ignore blank lines and comment lines.
198 		 */
199 		p = line;
200 		while (*p == ' ' || *p == '\t')
201 			++p;
202 		for (i = 0;  p[i] != '\n' && p[i] != '\0';  i++)
203 			;
204 		p[i] = '\0';
205 		if (*p == '#' || *p == '\0')
206 			continue;
207 
208 		/*
209 		 * Parse the command string and store it in the usertable.
210 		 */
211 		currcmd = up;
212 		do
213 		{
214 			if (up >= usertable + MAX_USERCMD)
215 			{
216 				fprintf(stderr, "too many commands, line %d\n",
217 					linenum);
218 				exit(1);
219 			}
220 			if (up >= currcmd + MAX_CMDLEN)
221 			{
222 				fprintf(stderr, "command too long on line %d\n",
223 					linenum);
224 				errors++;
225 				break;
226 			}
227 
228 			*up++ = tchar(&p);
229 
230 		} while (*p != ' ' && *p != '\t' && *p != '\0');
231 
232 		/*
233 		 * Terminate the command string with a null byte.
234 		 */
235 		*up++ = '\0';
236 
237 		/*
238 		 * Skip white space between the command string
239 		 * and the action name.
240 		 * Terminate the action name if it is followed
241 		 * by whitespace or a # comment.
242 		 */
243 		if (*p == '\0')
244 		{
245 			fprintf(stderr, "missing whitespace on line %d\n",
246 				linenum);
247 			errors++;
248 			continue;
249 		}
250 		while (*p == ' ' || *p == '\t')
251 			++p;
252 		for (i = 0;  p[i] != ' ' && p[i] != '\t' &&
253 			     p[i] != '#' && p[i] != '\0';  i++)
254 			;
255 		p[i] = '\0';
256 
257 		/*
258 		 * Parse the action name and store it in the usertable.
259 		 */
260 		for (i = 0;  cmdnames[i].cn_name != NULL;  i++)
261 			if (strcmp(cmdnames[i].cn_name, p) == 0)
262 				break;
263 		if (cmdnames[i].cn_name == NULL)
264 		{
265 			fprintf(stderr, "unknown action <%s> on line %d\n",
266 				p, linenum);
267 			errors++;
268 			continue;
269 		}
270 		*up++ = cmdnames[i].cn_action;
271 	}
272 
273 	if (errors > 0)
274 	{
275 		fprintf(stderr, "%d errors; no output produced\n", errors);
276 		exit(1);
277 	}
278 
279 	/*
280 	 * Write the output file.
281 	 * If no output file was specified, use "$HOME/.less"
282 	 */
283 	if (outfile == NULL)
284 	{
285 		p = getenv("HOME");
286 		if (p == NULL)
287 		{
288 			fprintf(stderr, "cannot find $HOME\n");
289 			exit(1);
290 		}
291 		strcpy(line, p);
292 		strcat(line, "/.less");
293 		outfile = line;
294 	}
295 	if ((out = fopen(outfile, "w")) == NULL)
296 		perror(outfile);
297 	else
298 		fwrite((char *)usertable, 1, up-usertable, out);
299 }
300 
301 /*
302  * Parse one character of the command string.
303  */
tchar(pp)304 tchar(pp)
305 	char **pp;
306 {
307 	register char *p;
308 	register char ch;
309 	register int i;
310 
311 	p = *pp;
312 	switch (*p)
313 	{
314 	case '\\':
315 		if (*++p >= '0' && *p <= '7')
316 		{
317 			/*
318 			 * Parse an octal number.
319 			 */
320 			ch = 0;
321 			i = 0;
322 			do
323 				ch = 8*ch + (*p - '0');
324 			while (*++p >= '0' && *p <= '7' && ++i < 3);
325 			*pp = p;
326 			return (ch);
327 		}
328 		/*
329 		 * Backslash followed by a char just means that char.
330 		 */
331 		*pp = p+1;
332 		return (*p);
333 	case '^':
334 		/*
335 		 * Carat means CONTROL.
336 		 */
337 		*pp = p+2;
338 		return (CONTROL(p[1]));
339 	}
340 	*pp = p+1;
341 	return (*p);
342 }
343 
usage()344 usage()
345 {
346 	fprintf(stderr, "usage: lesskey [-o output] [input]\n");
347 	exit(1);
348 }
349