=/^G show total bytes now; document may not always work
[unix-history] / usr / src / usr.bin / more / lesskey.c
CommitLineData
bfe13c81
KB
1/*
2 * Copyright (c) 1988 Mark Nudleman
3 * Copyright (c) 1988 Regents of the University of California.
4 * All rights reserved.
5 *
bfe13c81
KB
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
a942b40b
KB
11 * by Mark Nudleman and the University of California, Berkeley. The
12 * name of Mark Nudleman or the
bfe13c81
KB
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
21char 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
a942b40b 28static char sccsid[] = "@(#)lesskey.c 5.3 (Berkeley) %G%";
bfe13c81
KB
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
80char usertable[MAX_USERCMD];
81
82struct 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
127main(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
39525363 142 extern char *getenv(), *strcat(), *strcpy();
bfe13c81
KB
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 */
304tchar(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
344usage()
345{
346 fprintf(stderr, "usage: lesskey [-o output] [input]\n");
347 exit(1);
348}