Annotation of src/usr.bin/less/lesskey.c, Revision 1.8
1.1 etheisen 1: /*
1.7 shadchin 2: * Copyright (C) 1984-2012 Mark Nudelman
1.5 millert 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.
1.1 etheisen 6: *
1.7 shadchin 7: * For more information, see the README file.
1.1 etheisen 8: */
1.8 ! nicm 9: /*
! 10: * Modified for use with illumos.
! 11: * Copyright 2014 Garrett D'Amore <garrett@damore.org>
! 12: */
1.1 etheisen 13:
14: /*
15: * lesskey [-o output] [input]
16: *
1.8 ! nicm 17: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1.1 etheisen 18: *
19: * Make a .less file.
20: * If no input file is specified, standard input is used.
21: * If no output file is specified, $HOME/.less is used.
22: *
23: * The .less file is used to specify (to "less") user-defined
24: * key bindings. Basically any sequence of 1 to MAX_CMDLEN
25: * keystrokes may be bound to an existing less function.
26: *
1.8 ! nicm 27: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1.1 etheisen 28: *
1.8 ! nicm 29: * The input file is an ascii file consisting of a
1.1 etheisen 30: * sequence of lines of the form:
31: * string <whitespace> action [chars] <newline>
32: *
33: * "string" is a sequence of command characters which form
34: * the new user-defined command. The command
35: * characters may be:
36: * 1. The actual character itself.
37: * 2. A character preceded by ^ to specify a
38: * control character (e.g. ^X means control-X).
39: * 3. A backslash followed by one to three octal digits
40: * to specify a character by its octal value.
41: * 4. A backslash followed by b, e, n, r or t
42: * to specify \b, ESC, \n, \r or \t, respectively.
1.8 ! nicm 43: * 5. Any character (other than those mentioned above) preceded
1.1 etheisen 44: * by a \ to specify the character itself (characters which
45: * must be preceded by \ include ^, \, and whitespace.
46: * "action" is the name of a "less" action, from the table below.
47: * "chars" is an optional sequence of characters which is treated
48: * as keyboard input after the command is executed.
49: *
1.8 ! nicm 50: * Blank lines and lines which start with # are ignored,
1.1 etheisen 51: * except for the special control lines:
1.5 millert 52: * #command Signals the beginning of the command
53: * keys section.
1.1 etheisen 54: * #line-edit Signals the beginning of the line-editing
55: * keys section.
1.5 millert 56: * #env Signals the beginning of the environment
57: * variable section.
1.1 etheisen 58: * #stop Stops command parsing in less;
59: * causes all default keys to be disabled.
60: *
1.8 ! nicm 61: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1.1 etheisen 62: *
63: * The output file is a non-ascii file, consisting of a header,
64: * one or more sections, and a trailer.
65: * Each section begins with a section header, a section length word
66: * and the section data. Normally there are three sections:
67: * CMD_SECTION Definition of command keys.
68: * EDIT_SECTION Definition of editing keys.
1.8 ! nicm 69: * END_SECTION A special section header, with no
1.1 etheisen 70: * length word or section data.
71: *
72: * Section data consists of zero or more byte sequences of the form:
73: * string <0> <action>
74: * or
75: * string <0> <action|A_EXTRA> chars <0>
76: *
77: * "string" is the command string.
78: * "<0>" is one null byte.
79: * "<action>" is one byte containing the action code (the A_xxx value).
80: * If action is ORed with A_EXTRA, the action byte is followed
81: * by the null-terminated "chars" string.
82: *
1.8 ! nicm 83: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1.1 etheisen 84: */
85:
86: #include "less.h"
87: #include "lesskey.h"
88: #include "cmd.h"
89:
90: struct cmdname
91: {
92: char *cn_name;
93: int cn_action;
94: };
95:
1.8 ! nicm 96: static void lkerr(char *s);
! 97:
! 98: struct cmdname cmdnames[] =
1.1 etheisen 99: {
1.8 ! nicm 100: { "back-bracket", A_B_BRACKET },
! 101: { "back-line", A_B_LINE },
! 102: { "back-line-force", A_BF_LINE },
! 103: { "back-screen", A_B_SCREEN },
! 104: { "back-scroll", A_B_SCROLL },
! 105: { "back-search", A_B_SEARCH },
! 106: { "back-window", A_B_WINDOW },
! 107: { "debug", A_DEBUG },
! 108: { "digit", A_DIGIT },
! 109: { "display-flag", A_DISP_OPTION },
! 110: { "display-option", A_DISP_OPTION },
! 111: { "end", A_GOEND },
! 112: { "examine", A_EXAMINE },
! 113: { "filter", A_FILTER },
! 114: { "first-cmd", A_FIRSTCMD },
! 115: { "firstcmd", A_FIRSTCMD },
! 116: { "flush-repaint", A_FREPAINT },
! 117: { "forw-bracket", A_F_BRACKET },
! 118: { "forw-forever", A_F_FOREVER },
! 119: { "forw-until-hilite", A_F_UNTIL_HILITE },
! 120: { "forw-line", A_F_LINE },
! 121: { "forw-line-force", A_FF_LINE },
! 122: { "forw-screen", A_F_SCREEN },
! 123: { "forw-screen-force", A_FF_SCREEN },
! 124: { "forw-scroll", A_F_SCROLL },
! 125: { "forw-search", A_F_SEARCH },
! 126: { "forw-skip", A_F_SKIP },
! 127: { "forw-window", A_F_WINDOW },
! 128: { "goto-end", A_GOEND },
! 129: { "goto-line", A_GOLINE },
! 130: { "goto-mark", A_GOMARK },
! 131: { "help", A_HELP },
! 132: { "index-file", A_INDEX_FILE },
! 133: { "invalid", A_UINVALID },
! 134: { "left-scroll", A_LSHIFT },
! 135: { "next-file", A_NEXT_FILE },
! 136: { "next-tag", A_NEXT_TAG },
! 137: { "noaction", A_NOACTION },
! 138: { "percent", A_PERCENT },
! 139: { "pipe", A_PIPE },
! 140: { "prev-file", A_PREV_FILE },
! 141: { "prev-tag", A_PREV_TAG },
! 142: { "quit", A_QUIT },
! 143: { "remove-file", A_REMOVE_FILE },
! 144: { "repaint", A_REPAINT },
! 145: { "repaint-flush", A_FREPAINT },
! 146: { "repeat-search", A_AGAIN_SEARCH },
! 147: { "repeat-search-all", A_T_AGAIN_SEARCH },
! 148: { "reverse-search", A_REVERSE_SEARCH },
! 149: { "reverse-search-all", A_T_REVERSE_SEARCH },
! 150: { "right-scroll", A_RSHIFT },
! 151: { "set-mark", A_SETMARK },
! 152: { "shell", A_SHELL },
! 153: { "status", A_STAT },
! 154: { "toggle-flag", A_OPT_TOGGLE },
! 155: { "toggle-option", A_OPT_TOGGLE },
! 156: { "undo-hilite", A_UNDO_SEARCH },
! 157: { "version", A_VERSION },
! 158: { "visual", A_VISUAL },
! 159: { NULL, 0 }
1.1 etheisen 160: };
161:
1.8 ! nicm 162: struct cmdname editnames[] = {
1.5 millert 163: { "back-complete", EC_B_COMPLETE },
164: { "backspace", EC_BACKSPACE },
165: { "delete", EC_DELETE },
166: { "down", EC_DOWN },
167: { "end", EC_END },
168: { "expand", EC_EXPAND },
169: { "forw-complete", EC_F_COMPLETE },
170: { "home", EC_HOME },
171: { "insert", EC_INSERT },
172: { "invalid", EC_UINVALID },
173: { "kill-line", EC_LINEKILL },
1.6 shadchin 174: { "abort", EC_ABORT },
1.5 millert 175: { "left", EC_LEFT },
176: { "literal", EC_LITERAL },
177: { "right", EC_RIGHT },
178: { "up", EC_UP },
179: { "word-backspace", EC_W_BACKSPACE },
180: { "word-delete", EC_W_DELETE },
181: { "word-left", EC_W_LEFT },
182: { "word-right", EC_W_RIGHT },
183: { NULL, 0 }
1.1 etheisen 184: };
185:
186: struct table
187: {
188: struct cmdname *names;
189: char *pbuffer;
190: char buffer[MAX_USERCMD];
191: };
192:
193: struct table cmdtable;
194: struct table edittable;
1.5 millert 195: struct table vartable;
1.1 etheisen 196: struct table *currtable = &cmdtable;
197:
198: char fileheader[] = {
1.8 ! nicm 199: C0_LESSKEY_MAGIC,
! 200: C1_LESSKEY_MAGIC,
! 201: C2_LESSKEY_MAGIC,
1.1 etheisen 202: C3_LESSKEY_MAGIC
203: };
204: char filetrailer[] = {
1.8 ! nicm 205: C0_END_LESSKEY_MAGIC,
! 206: C1_END_LESSKEY_MAGIC,
1.1 etheisen 207: C2_END_LESSKEY_MAGIC
208: };
209: char cmdsection[1] = { CMD_SECTION };
210: char editsection[1] = { EDIT_SECTION };
1.5 millert 211: char varsection[1] = { VAR_SECTION };
1.1 etheisen 212: char endsection[1] = { END_SECTION };
213:
214: char *infile = NULL;
1.8 ! nicm 215: char *outfile = NULL;
1.1 etheisen 216:
217: int linenum;
218: int errors;
219:
220: extern char version[];
221:
1.8 ! nicm 222: void
! 223: usage(void)
1.5 millert 224: {
1.8 ! nicm 225: (void) fprintf(stderr, "usage: lesskey [-o output] [input]\n");
1.5 millert 226: exit(1);
227: }
228:
1.8 ! nicm 229: char *
! 230: mkpathname(char *dirname, char *filename)
1.1 etheisen 231: {
232: char *pathname;
1.4 deraadt 233: size_t len;
1.1 etheisen 234:
1.4 deraadt 235: len = strlen(dirname) + strlen(filename) + 2;
1.8 ! nicm 236: pathname = calloc(1, len);
! 237: if (pathname == NULL) {
! 238: fprintf(stderr, "mkpathname: out of memory\n");
! 239: exit(1);
! 240: }
! 241: (void) snprintf(pathname, len, "%s/%s", dirname, filename);
1.1 etheisen 242: return (pathname);
243: }
244:
245: /*
246: * Figure out the name of a default file (in the user's HOME directory).
247: */
1.8 ! nicm 248: char *
! 249: homefile(char *filename)
1.1 etheisen 250: {
251: char *p;
252: char *pathname;
253:
1.8 ! nicm 254: if ((p = getenv("HOME")) != NULL && *p != '\0') {
1.1 etheisen 255: pathname = mkpathname(p, filename);
1.8 ! nicm 256: } else {
! 257: (void) fprintf(stderr, "cannot find $HOME - "
! 258: "using current directory\n");
1.1 etheisen 259: pathname = mkpathname(".", filename);
260: }
261: return (pathname);
262: }
263:
264: /*
265: * Parse command line arguments.
266: */
1.8 ! nicm 267: void
! 268: parse_args(int argc, char **argv)
1.1 etheisen 269: {
1.5 millert 270: char *arg;
271:
1.1 etheisen 272: outfile = NULL;
1.8 ! nicm 273: while (--argc > 0) {
1.5 millert 274: arg = *++argv;
275: if (arg[0] != '-')
276: /* Arg does not start with "-"; it's not an option. */
277: break;
278: if (arg[1] == '\0')
279: /* "-" means standard input. */
280: break;
1.8 ! nicm 281: if (arg[1] == '-' && arg[2] == '\0') {
1.5 millert 282: /* "--" means end of options. */
283: argc--;
284: argv++;
285: break;
286: }
1.8 ! nicm 287: switch (arg[1]) {
1.5 millert 288: case '-':
1.8 ! nicm 289: if (strncmp(arg, "--output", 8) == 0) {
1.5 millert 290: if (arg[8] == '\0')
291: outfile = &arg[8];
292: else if (arg[8] == '=')
293: outfile = &arg[9];
294: else
295: usage();
296: goto opt_o;
297: }
1.8 ! nicm 298: if (strcmp(arg, "--version") == 0) {
1.5 millert 299: goto opt_V;
300: }
301: usage();
302: break;
1.1 etheisen 303: case 'o':
304: outfile = &argv[0][2];
1.5 millert 305: opt_o:
1.8 ! nicm 306: if (*outfile == '\0') {
1.1 etheisen 307: if (--argc <= 0)
308: usage();
309: outfile = *(++argv);
310: }
311: break;
312: case 'V':
1.5 millert 313: opt_V:
1.8 ! nicm 314: (void) printf("lesskey version %s\n", version);
1.1 etheisen 315: exit(0);
316: default:
317: usage();
318: }
319: }
320: if (argc > 1)
321: usage();
322: /*
323: * Open the input file, or use DEF_LESSKEYINFILE if none specified.
324: */
325: if (argc > 0)
326: infile = *argv;
327: else
328: infile = homefile(DEF_LESSKEYINFILE);
329: }
330:
331: /*
332: * Initialize data structures.
333: */
1.8 ! nicm 334: void
! 335: init_tables(void)
1.1 etheisen 336: {
337: cmdtable.names = cmdnames;
338: cmdtable.pbuffer = cmdtable.buffer;
339:
340: edittable.names = editnames;
341: edittable.pbuffer = edittable.buffer;
1.5 millert 342:
343: vartable.names = NULL;
344: vartable.pbuffer = vartable.buffer;
1.1 etheisen 345: }
346:
347: /*
348: * Parse one character of a string.
349: */
1.8 ! nicm 350: char *
! 351: tstr(char **pp, int xlate)
! 352: {
! 353: char *p;
! 354: char ch;
! 355: int i;
1.5 millert 356: static char buf[10];
357: static char tstr_control_k[] =
358: { SK_SPECIAL_KEY, SK_CONTROL_K, 6, 1, 1, 1, '\0' };
1.1 etheisen 359:
360: p = *pp;
1.8 ! nicm 361: switch (*p) {
1.1 etheisen 362: case '\\':
363: ++p;
1.8 ! nicm 364: switch (*p) {
1.1 etheisen 365: case '0': case '1': case '2': case '3':
366: case '4': case '5': case '6': case '7':
367: /*
368: * Parse an octal number.
369: */
370: ch = 0;
371: i = 0;
372: do
373: ch = 8*ch + (*p - '0');
1.8 ! nicm 374: while (*++p >= '0' && *p <= '7' && ++i < 3)
! 375: ;
1.1 etheisen 376: *pp = p;
1.5 millert 377: if (xlate && ch == CONTROL('K'))
1.8 ! nicm 378: return (tstr_control_k);
1.5 millert 379: buf[0] = ch;
380: buf[1] = '\0';
381: return (buf);
1.1 etheisen 382: case 'b':
383: *pp = p+1;
1.5 millert 384: return ("\b");
1.1 etheisen 385: case 'e':
386: *pp = p+1;
1.5 millert 387: buf[0] = ESC;
388: buf[1] = '\0';
389: return (buf);
1.1 etheisen 390: case 'n':
391: *pp = p+1;
1.5 millert 392: return ("\n");
1.1 etheisen 393: case 'r':
394: *pp = p+1;
1.5 millert 395: return ("\r");
1.1 etheisen 396: case 't':
397: *pp = p+1;
1.5 millert 398: return ("\t");
399: case 'k':
1.8 ! nicm 400: if (xlate) {
! 401: switch (*++p) {
1.5 millert 402: case 'u': ch = SK_UP_ARROW; break;
403: case 'd': ch = SK_DOWN_ARROW; break;
404: case 'r': ch = SK_RIGHT_ARROW; break;
405: case 'l': ch = SK_LEFT_ARROW; break;
406: case 'U': ch = SK_PAGE_UP; break;
407: case 'D': ch = SK_PAGE_DOWN; break;
408: case 'h': ch = SK_HOME; break;
409: case 'e': ch = SK_END; break;
410: case 'x': ch = SK_DELETE; break;
411: default:
1.8 ! nicm 412: lkerr("illegal char after \\k");
1.5 millert 413: *pp = p+1;
414: return ("");
415: }
416: *pp = p+1;
417: buf[0] = SK_SPECIAL_KEY;
418: buf[1] = ch;
419: buf[2] = 6;
420: buf[3] = 1;
421: buf[4] = 1;
422: buf[5] = 1;
423: buf[6] = '\0';
424: return (buf);
425: }
426: /* FALLTHRU */
1.1 etheisen 427: default:
428: /*
1.8 ! nicm 429: * Backslash followed by any other char
1.1 etheisen 430: * just means that char.
431: */
432: *pp = p+1;
1.5 millert 433: buf[0] = *p;
434: buf[1] = '\0';
435: if (xlate && buf[0] == CONTROL('K'))
1.8 ! nicm 436: return (tstr_control_k);
1.5 millert 437: return (buf);
1.1 etheisen 438: }
439: case '^':
440: /*
1.7 shadchin 441: * Caret means CONTROL.
1.1 etheisen 442: */
443: *pp = p+2;
1.5 millert 444: buf[0] = CONTROL(p[1]);
445: buf[1] = '\0';
446: if (buf[0] == CONTROL('K'))
1.8 ! nicm 447: return (tstr_control_k);
1.5 millert 448: return (buf);
1.1 etheisen 449: }
450: *pp = p+1;
1.5 millert 451: buf[0] = *p;
452: buf[1] = '\0';
453: if (xlate && buf[0] == CONTROL('K'))
1.8 ! nicm 454: return (tstr_control_k);
1.5 millert 455: return (buf);
1.1 etheisen 456: }
457:
458: /*
459: * Skip leading spaces in a string.
460: */
1.8 ! nicm 461: char *
! 462: skipsp(char *s)
1.1 etheisen 463: {
1.8 ! nicm 464: while (*s == ' ' || *s == '\t')
1.1 etheisen 465: s++;
466: return (s);
467: }
468:
469: /*
470: * Skip non-space characters in a string.
471: */
1.8 ! nicm 472: char *
! 473: skipnsp(char *s)
1.1 etheisen 474: {
475: while (*s != '\0' && *s != ' ' && *s != '\t')
476: s++;
477: return (s);
478: }
479:
480: /*
481: * Clean up an input line:
482: * strip off the trailing newline & any trailing # comment.
483: */
1.8 ! nicm 484: char *
! 485: clean_line(char *s)
1.1 etheisen 486: {
1.8 ! nicm 487: int i;
1.1 etheisen 488:
489: s = skipsp(s);
1.5 millert 490: for (i = 0; s[i] != '\n' && s[i] != '\r' && s[i] != '\0'; i++)
1.1 etheisen 491: if (s[i] == '#' && (i == 0 || s[i-1] != '\\'))
492: break;
493: s[i] = '\0';
494: return (s);
495: }
496:
497: /*
498: * Add a byte to the output command table.
499: */
1.8 ! nicm 500: void
! 501: add_cmd_char(int c)
! 502: {
! 503: if (currtable->pbuffer >= currtable->buffer + MAX_USERCMD) {
! 504: lkerr("too many commands");
1.1 etheisen 505: exit(1);
506: }
1.8 ! nicm 507: *(currtable->pbuffer)++ = (char)c;
1.1 etheisen 508: }
509:
510: /*
1.5 millert 511: * Add a string to the output command table.
512: */
1.8 ! nicm 513: void
! 514: add_cmd_str(char *s)
1.5 millert 515: {
1.8 ! nicm 516: for (; *s != '\0'; s++)
1.5 millert 517: add_cmd_char(*s);
518: }
519:
520: /*
1.1 etheisen 521: * See if we have a special "control" line.
522: */
1.8 ! nicm 523: int
! 524: control_line(char *s)
1.1 etheisen 525: {
1.8 ! nicm 526: #define PREFIX(str, pat) (strncmp(str, pat, strlen(pat)) == 0)
1.1 etheisen 527:
1.8 ! nicm 528: if (PREFIX(s, "#line-edit")) {
1.1 etheisen 529: currtable = &edittable;
530: return (1);
531: }
1.8 ! nicm 532: if (PREFIX(s, "#command")) {
1.1 etheisen 533: currtable = &cmdtable;
534: return (1);
535: }
1.8 ! nicm 536: if (PREFIX(s, "#env")) {
1.5 millert 537: currtable = &vartable;
538: return (1);
539: }
1.8 ! nicm 540: if (PREFIX(s, "#stop")) {
1.1 etheisen 541: add_cmd_char('\0');
542: add_cmd_char(A_END_LIST);
543: return (1);
544: }
545: return (0);
546: }
547:
548: /*
549: * Output some bytes.
550: */
1.8 ! nicm 551: void
! 552: fputbytes(FILE *fd, char *buf, int len)
! 553: {
! 554: while (len-- > 0) {
! 555: (void) fwrite(buf, sizeof (char), 1, fd);
1.1 etheisen 556: buf++;
557: }
558: }
559:
560: /*
561: * Output an integer, in special KRADIX form.
562: */
1.8 ! nicm 563: void
! 564: fputint(FILE *fd, unsigned int val)
1.1 etheisen 565: {
566: char c;
567:
1.8 ! nicm 568: if (val >= KRADIX*KRADIX) {
! 569: (void) fprintf(stderr, "error: integer too big (%d > %d)\n",
! 570: val, KRADIX*KRADIX);
1.1 etheisen 571: exit(1);
572: }
573: c = val % KRADIX;
1.8 ! nicm 574: (void) fwrite(&c, sizeof (char), 1, fd);
1.1 etheisen 575: c = val / KRADIX;
1.8 ! nicm 576: (void) fwrite(&c, sizeof (char), 1, fd);
1.1 etheisen 577: }
578:
579: /*
580: * Find an action, given the name of the action.
581: */
1.8 ! nicm 582: int
! 583: findaction(char *actname)
1.1 etheisen 584: {
585: int i;
586:
587: for (i = 0; currtable->names[i].cn_name != NULL; i++)
588: if (strcmp(currtable->names[i].cn_name, actname) == 0)
589: return (currtable->names[i].cn_action);
1.8 ! nicm 590: lkerr("unknown action");
1.1 etheisen 591: return (A_INVALID);
592: }
593:
1.8 ! nicm 594: void
! 595: lkerr(char *s)
1.1 etheisen 596: {
1.8 ! nicm 597: (void) fprintf(stderr, "line %d: %s\n", linenum, s);
1.1 etheisen 598: errors++;
599: }
600:
601:
1.8 ! nicm 602: void
! 603: parse_cmdline(char *p)
1.1 etheisen 604: {
605: int cmdlen;
606: char *actname;
607: int action;
1.5 millert 608: char *s;
609: char c;
1.1 etheisen 610:
611: /*
612: * Parse the command string and store it in the current table.
613: */
614: cmdlen = 0;
1.8 ! nicm 615: do {
1.5 millert 616: s = tstr(&p, 1);
617: cmdlen += strlen(s);
618: if (cmdlen > MAX_CMDLEN)
1.8 ! nicm 619: lkerr("command too long");
1.1 etheisen 620: else
1.5 millert 621: add_cmd_str(s);
1.1 etheisen 622: } while (*p != ' ' && *p != '\t' && *p != '\0');
623: /*
624: * Terminate the command string with a null byte.
625: */
626: add_cmd_char('\0');
627:
628: /*
629: * Skip white space between the command string
630: * and the action name.
631: * Terminate the action name with a null byte.
632: */
633: p = skipsp(p);
1.8 ! nicm 634: if (*p == '\0') {
! 635: lkerr("missing action");
1.1 etheisen 636: return;
637: }
638: actname = p;
639: p = skipnsp(p);
640: c = *p;
641: *p = '\0';
642:
643: /*
644: * Parse the action name and store it in the current table.
645: */
646: action = findaction(actname);
647:
648: /*
649: * See if an extra string follows the action name.
650: */
651: *p = c;
652: p = skipsp(p);
1.8 ! nicm 653: if (*p == '\0') {
1.1 etheisen 654: add_cmd_char(action);
1.8 ! nicm 655: } else {
1.1 etheisen 656: /*
657: * OR the special value A_EXTRA into the action byte.
658: * Put the extra string after the action byte.
659: */
660: add_cmd_char(action | A_EXTRA);
661: while (*p != '\0')
1.5 millert 662: add_cmd_str(tstr(&p, 0));
1.1 etheisen 663: add_cmd_char('\0');
664: }
665: }
666:
1.8 ! nicm 667: void
! 668: parse_varline(char *p)
1.5 millert 669: {
670: char *s;
671:
1.8 ! nicm 672: do {
1.5 millert 673: s = tstr(&p, 0);
674: add_cmd_str(s);
675: } while (*p != ' ' && *p != '\t' && *p != '=' && *p != '\0');
676: /*
677: * Terminate the variable name with a null byte.
678: */
679: add_cmd_char('\0');
680:
681: p = skipsp(p);
1.8 ! nicm 682: if (*p++ != '=') {
! 683: lkerr("missing =");
1.5 millert 684: return;
685: }
686:
687: add_cmd_char(EV_OK|A_EXTRA);
688:
689: p = skipsp(p);
1.8 ! nicm 690: while (*p != '\0') {
1.5 millert 691: s = tstr(&p, 0);
692: add_cmd_str(s);
693: }
694: add_cmd_char('\0');
695: }
696:
697: /*
698: * Parse a line from the lesskey file.
699: */
1.8 ! nicm 700: void
! 701: parse_line(char *line)
1.5 millert 702: {
703: char *p;
704:
705: /*
706: * See if it is a control line.
707: */
708: if (control_line(line))
709: return;
710: /*
711: * Skip leading white space.
712: * Replace the final newline with a null byte.
713: * Ignore blank lines and comments.
714: */
715: p = clean_line(line);
716: if (*p == '\0')
717: return;
718:
719: if (currtable == &vartable)
720: parse_varline(p);
721: else
722: parse_cmdline(p);
723: }
724:
1.8 ! nicm 725: int
! 726: main(int argc, char **argv)
1.1 etheisen 727: {
728: FILE *desc;
729: FILE *out;
1.5 millert 730: char line[1024];
731:
1.1 etheisen 732: /*
733: * Process command line arguments.
734: */
735: parse_args(argc, argv);
736: init_tables();
737:
738: /*
739: * Open the input file.
740: */
741: if (strcmp(infile, "-") == 0)
742: desc = stdin;
1.8 ! nicm 743: else if ((desc = fopen(infile, "r")) == NULL) {
1.1 etheisen 744: perror(infile);
1.5 millert 745: usage();
1.1 etheisen 746: }
747:
748: /*
749: * Read and parse the input file, one line at a time.
750: */
751: errors = 0;
752: linenum = 0;
1.8 ! nicm 753: while (fgets(line, sizeof (line), desc) != NULL) {
1.1 etheisen 754: ++linenum;
755: parse_line(line);
756: }
757:
758: /*
759: * Write the output file.
760: * If no output file was specified, use "$HOME/.less"
761: */
1.8 ! nicm 762: if (errors > 0) {
! 763: (void) fprintf(stderr, "%d errors; no output produced\n",
! 764: errors);
1.1 etheisen 765: exit(1);
766: }
767:
768: if (outfile == NULL)
1.5 millert 769: outfile = getenv("LESSKEY");
770: if (outfile == NULL)
1.1 etheisen 771: outfile = homefile(LESSKEYFILE);
1.8 ! nicm 772: if ((out = fopen(outfile, "wb")) == NULL) {
1.1 etheisen 773: perror(outfile);
774: exit(1);
775: }
776:
777: /* File header */
1.8 ! nicm 778: fputbytes(out, fileheader, sizeof (fileheader));
1.1 etheisen 779:
780: /* Command key section */
1.8 ! nicm 781: fputbytes(out, cmdsection, sizeof (cmdsection));
1.1 etheisen 782: fputint(out, cmdtable.pbuffer - cmdtable.buffer);
1.8 ! nicm 783: fputbytes(out, (char *)cmdtable.buffer,
! 784: cmdtable.pbuffer-cmdtable.buffer);
! 785:
1.1 etheisen 786: /* Edit key section */
1.8 ! nicm 787: fputbytes(out, editsection, sizeof (editsection));
1.1 etheisen 788: fputint(out, edittable.pbuffer - edittable.buffer);
1.8 ! nicm 789: fputbytes(out, (char *)edittable.buffer,
! 790: edittable.pbuffer-edittable.buffer);
1.1 etheisen 791:
1.5 millert 792: /* Environment variable section */
1.8 ! nicm 793: fputbytes(out, varsection, sizeof (varsection));
1.5 millert 794: fputint(out, vartable.pbuffer - vartable.buffer);
1.8 ! nicm 795: fputbytes(out, (char *)vartable.buffer,
! 796: vartable.pbuffer-vartable.buffer);
1.5 millert 797:
1.1 etheisen 798: /* File trailer */
1.8 ! nicm 799: fputbytes(out, endsection, sizeof (endsection));
! 800: fputbytes(out, filetrailer, sizeof (filetrailer));
1.5 millert 801: return (0);
1.1 etheisen 802: }