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