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