Annotation of src/usr.bin/less/lesskey.c, Revision 1.1.1.3
1.1 etheisen 1: /*
1.1.1.3 ! shadchin 2: * Copyright (C) 1984-2011 Mark Nudelman
1.1 etheisen 3: *
1.1.1.2 millert 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.
6: *
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.1.1.2 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.1.1.2 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.1.1.3 ! 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.1.1.2 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.1.1.3 ! shadchin 169: { "abort", EC_ABORT },
1.1.1.2 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.1.1.2 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.1.1.2 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.1.1.2 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;
230:
231: pathname = calloc(strlen(dirname) + strlen(filename) + 2, sizeof(char));
232: strcpy(pathname, dirname);
1.1.1.2 millert 233: strcat(pathname, PATHNAME_SEP);
1.1 etheisen 234: strcat(pathname, filename);
235: return (pathname);
236: }
237:
238: /*
239: * Figure out the name of a default file (in the user's HOME directory).
240: */
241: char *
242: homefile(filename)
243: char *filename;
244: {
245: char *p;
246: char *pathname;
247:
248: if ((p = getenv("HOME")) != NULL && *p != '\0')
249: pathname = mkpathname(p, filename);
250: #if OS2
251: else if ((p = getenv("INIT")) != NULL && *p != '\0')
252: pathname = mkpathname(p, filename);
253: #endif
254: else
255: {
256: fprintf(stderr, "cannot find $HOME - using current directory\n");
257: pathname = mkpathname(".", filename);
258: }
259: return (pathname);
260: }
261:
262: /*
263: * Parse command line arguments.
264: */
265: void
266: parse_args(argc, argv)
267: int argc;
268: char **argv;
269: {
1.1.1.2 millert 270: char *arg;
271:
1.1 etheisen 272: outfile = NULL;
1.1.1.2 millert 273: while (--argc > 0)
1.1 etheisen 274: {
1.1.1.2 millert 275: arg = *++argv;
276: if (arg[0] != '-')
277: /* Arg does not start with "-"; it's not an option. */
278: break;
279: if (arg[1] == '\0')
280: /* "-" means standard input. */
281: break;
282: if (arg[1] == '-' && arg[2] == '\0')
283: {
284: /* "--" means end of options. */
285: argc--;
286: argv++;
287: break;
288: }
289: switch (arg[1])
1.1 etheisen 290: {
1.1.1.2 millert 291: case '-':
292: if (strncmp(arg, "--output", 8) == 0)
293: {
294: if (arg[8] == '\0')
295: outfile = &arg[8];
296: else if (arg[8] == '=')
297: outfile = &arg[9];
298: else
299: usage();
300: goto opt_o;
301: }
302: if (strcmp(arg, "--version") == 0)
303: {
304: goto opt_V;
305: }
306: usage();
307: break;
1.1 etheisen 308: case 'o':
309: outfile = &argv[0][2];
1.1.1.2 millert 310: opt_o:
1.1 etheisen 311: if (*outfile == '\0')
312: {
313: if (--argc <= 0)
314: usage();
315: outfile = *(++argv);
316: }
317: break;
318: case 'V':
1.1.1.2 millert 319: opt_V:
1.1 etheisen 320: printf("lesskey version %s\n", version);
321: exit(0);
322: default:
323: usage();
324: }
325: }
326: if (argc > 1)
327: usage();
328: /*
329: * Open the input file, or use DEF_LESSKEYINFILE if none specified.
330: */
331: if (argc > 0)
332: infile = *argv;
333: else
334: infile = homefile(DEF_LESSKEYINFILE);
335: }
336:
337: /*
338: * Initialize data structures.
339: */
340: void
341: init_tables()
342: {
343: cmdtable.names = cmdnames;
344: cmdtable.pbuffer = cmdtable.buffer;
345:
346: edittable.names = editnames;
347: edittable.pbuffer = edittable.buffer;
1.1.1.2 millert 348:
349: vartable.names = NULL;
350: vartable.pbuffer = vartable.buffer;
1.1 etheisen 351: }
352:
353: /*
354: * Parse one character of a string.
355: */
1.1.1.2 millert 356: char *
357: tstr(pp, xlate)
1.1 etheisen 358: char **pp;
1.1.1.2 millert 359: int xlate;
1.1 etheisen 360: {
361: register char *p;
362: register char ch;
363: register int i;
1.1.1.2 millert 364: static char buf[10];
365: static char tstr_control_k[] =
366: { SK_SPECIAL_KEY, SK_CONTROL_K, 6, 1, 1, 1, '\0' };
1.1 etheisen 367:
368: p = *pp;
369: switch (*p)
370: {
371: case '\\':
372: ++p;
373: switch (*p)
374: {
375: case '0': case '1': case '2': case '3':
376: case '4': case '5': case '6': case '7':
377: /*
378: * Parse an octal number.
379: */
380: ch = 0;
381: i = 0;
382: do
383: ch = 8*ch + (*p - '0');
384: while (*++p >= '0' && *p <= '7' && ++i < 3);
385: *pp = p;
1.1.1.2 millert 386: if (xlate && ch == CONTROL('K'))
387: return tstr_control_k;
388: buf[0] = ch;
389: buf[1] = '\0';
390: return (buf);
1.1 etheisen 391: case 'b':
392: *pp = p+1;
1.1.1.2 millert 393: return ("\b");
1.1 etheisen 394: case 'e':
395: *pp = p+1;
1.1.1.2 millert 396: buf[0] = ESC;
397: buf[1] = '\0';
398: return (buf);
1.1 etheisen 399: case 'n':
400: *pp = p+1;
1.1.1.2 millert 401: return ("\n");
1.1 etheisen 402: case 'r':
403: *pp = p+1;
1.1.1.2 millert 404: return ("\r");
1.1 etheisen 405: case 't':
406: *pp = p+1;
1.1.1.2 millert 407: return ("\t");
408: case 'k':
409: if (xlate)
410: {
411: switch (*++p)
412: {
413: case 'u': ch = SK_UP_ARROW; break;
414: case 'd': ch = SK_DOWN_ARROW; break;
415: case 'r': ch = SK_RIGHT_ARROW; break;
416: case 'l': ch = SK_LEFT_ARROW; break;
417: case 'U': ch = SK_PAGE_UP; break;
418: case 'D': ch = SK_PAGE_DOWN; break;
419: case 'h': ch = SK_HOME; break;
420: case 'e': ch = SK_END; break;
421: case 'x': ch = SK_DELETE; break;
422: default:
423: error("illegal char after \\k");
424: *pp = p+1;
425: return ("");
426: }
427: *pp = p+1;
428: buf[0] = SK_SPECIAL_KEY;
429: buf[1] = ch;
430: buf[2] = 6;
431: buf[3] = 1;
432: buf[4] = 1;
433: buf[5] = 1;
434: buf[6] = '\0';
435: return (buf);
436: }
437: /* FALLTHRU */
1.1 etheisen 438: default:
439: /*
440: * Backslash followed by any other char
441: * just means that char.
442: */
443: *pp = p+1;
1.1.1.2 millert 444: buf[0] = *p;
445: buf[1] = '\0';
446: if (xlate && buf[0] == CONTROL('K'))
447: return tstr_control_k;
448: return (buf);
1.1 etheisen 449: }
450: case '^':
451: /*
452: * Carat means CONTROL.
453: */
454: *pp = p+2;
1.1.1.2 millert 455: buf[0] = CONTROL(p[1]);
456: buf[1] = '\0';
457: if (buf[0] == CONTROL('K'))
458: return tstr_control_k;
459: return (buf);
1.1 etheisen 460: }
461: *pp = p+1;
1.1.1.2 millert 462: buf[0] = *p;
463: buf[1] = '\0';
464: if (xlate && buf[0] == CONTROL('K'))
465: return tstr_control_k;
466: return (buf);
1.1 etheisen 467: }
468:
469: /*
470: * Skip leading spaces in a string.
471: */
472: public char *
473: skipsp(s)
474: register char *s;
475: {
476: while (*s == ' ' || *s == '\t')
477: s++;
478: return (s);
479: }
480:
481: /*
482: * Skip non-space characters in a string.
483: */
484: public char *
485: skipnsp(s)
486: register char *s;
487: {
488: while (*s != '\0' && *s != ' ' && *s != '\t')
489: s++;
490: return (s);
491: }
492:
493: /*
494: * Clean up an input line:
495: * strip off the trailing newline & any trailing # comment.
496: */
497: char *
498: clean_line(s)
499: char *s;
500: {
501: register int i;
502:
503: s = skipsp(s);
1.1.1.2 millert 504: for (i = 0; s[i] != '\n' && s[i] != '\r' && s[i] != '\0'; i++)
1.1 etheisen 505: if (s[i] == '#' && (i == 0 || s[i-1] != '\\'))
506: break;
507: s[i] = '\0';
508: return (s);
509: }
510:
511: /*
512: * Add a byte to the output command table.
513: */
514: void
515: add_cmd_char(c)
516: int c;
517: {
518: if (currtable->pbuffer >= currtable->buffer + MAX_USERCMD)
519: {
520: error("too many commands");
521: exit(1);
522: }
523: *(currtable->pbuffer)++ = c;
524: }
525:
526: /*
1.1.1.2 millert 527: * Add a string to the output command table.
528: */
529: void
530: add_cmd_str(s)
531: char *s;
532: {
533: for ( ; *s != '\0'; s++)
534: add_cmd_char(*s);
535: }
536:
537: /*
1.1 etheisen 538: * See if we have a special "control" line.
539: */
540: int
541: control_line(s)
542: char *s;
543: {
1.1.1.3 ! shadchin 544: #define PREFIX(str,pat) (strncmp(str,pat,strlen(pat)) == 0)
1.1 etheisen 545:
546: if (PREFIX(s, "#line-edit"))
547: {
548: currtable = &edittable;
549: return (1);
550: }
551: if (PREFIX(s, "#command"))
552: {
553: currtable = &cmdtable;
554: return (1);
555: }
1.1.1.2 millert 556: if (PREFIX(s, "#env"))
557: {
558: currtable = &vartable;
559: return (1);
560: }
1.1 etheisen 561: if (PREFIX(s, "#stop"))
562: {
563: add_cmd_char('\0');
564: add_cmd_char(A_END_LIST);
565: return (1);
566: }
567: return (0);
568: }
569:
570: /*
571: * Output some bytes.
572: */
573: void
574: fputbytes(fd, buf, len)
575: FILE *fd;
576: char *buf;
577: int len;
578: {
579: while (len-- > 0)
580: {
581: fwrite(buf, sizeof(char), 1, fd);
582: buf++;
583: }
584: }
585:
586: /*
587: * Output an integer, in special KRADIX form.
588: */
589: void
590: fputint(fd, val)
591: FILE *fd;
592: unsigned int val;
593: {
594: char c;
595:
596: if (val >= KRADIX*KRADIX)
597: {
598: fprintf(stderr, "error: integer too big (%d > %d)\n",
599: val, KRADIX*KRADIX);
600: exit(1);
601: }
602: c = val % KRADIX;
603: fwrite(&c, sizeof(char), 1, fd);
604: c = val / KRADIX;
605: fwrite(&c, sizeof(char), 1, fd);
606: }
607:
608: /*
609: * Find an action, given the name of the action.
610: */
611: int
612: findaction(actname)
613: char *actname;
614: {
615: int i;
616:
617: for (i = 0; currtable->names[i].cn_name != NULL; i++)
618: if (strcmp(currtable->names[i].cn_name, actname) == 0)
619: return (currtable->names[i].cn_action);
620: error("unknown action");
621: return (A_INVALID);
622: }
623:
624: void
625: error(s)
626: char *s;
627: {
628: fprintf(stderr, "line %d: %s\n", linenum, s);
629: errors++;
630: }
631:
632:
633: void
1.1.1.2 millert 634: parse_cmdline(p)
1.1 etheisen 635: char *p;
1.1.1.2 millert 636: {
1.1 etheisen 637: int cmdlen;
638: char *actname;
639: int action;
1.1.1.2 millert 640: char *s;
641: char c;
1.1 etheisen 642:
643: /*
644: * Parse the command string and store it in the current table.
645: */
646: cmdlen = 0;
647: do
648: {
1.1.1.2 millert 649: s = tstr(&p, 1);
650: cmdlen += strlen(s);
651: if (cmdlen > MAX_CMDLEN)
1.1 etheisen 652: error("command too long");
653: else
1.1.1.2 millert 654: add_cmd_str(s);
1.1 etheisen 655: } while (*p != ' ' && *p != '\t' && *p != '\0');
656: /*
657: * Terminate the command string with a null byte.
658: */
659: add_cmd_char('\0');
660:
661: /*
662: * Skip white space between the command string
663: * and the action name.
664: * Terminate the action name with a null byte.
665: */
666: p = skipsp(p);
667: if (*p == '\0')
668: {
669: error("missing action");
670: return;
671: }
672: actname = p;
673: p = skipnsp(p);
674: c = *p;
675: *p = '\0';
676:
677: /*
678: * Parse the action name and store it in the current table.
679: */
680: action = findaction(actname);
681:
682: /*
683: * See if an extra string follows the action name.
684: */
685: *p = c;
686: p = skipsp(p);
687: if (*p == '\0')
688: {
689: add_cmd_char(action);
690: } else
691: {
692: /*
693: * OR the special value A_EXTRA into the action byte.
694: * Put the extra string after the action byte.
695: */
696: add_cmd_char(action | A_EXTRA);
697: while (*p != '\0')
1.1.1.2 millert 698: add_cmd_str(tstr(&p, 0));
1.1 etheisen 699: add_cmd_char('\0');
700: }
701: }
702:
1.1.1.2 millert 703: void
704: parse_varline(p)
705: char *p;
706: {
707: char *s;
708:
709: do
710: {
711: s = tstr(&p, 0);
712: add_cmd_str(s);
713: } while (*p != ' ' && *p != '\t' && *p != '=' && *p != '\0');
714: /*
715: * Terminate the variable name with a null byte.
716: */
717: add_cmd_char('\0');
718:
719: p = skipsp(p);
720: if (*p++ != '=')
721: {
722: error("missing =");
723: return;
724: }
725:
726: add_cmd_char(EV_OK|A_EXTRA);
727:
728: p = skipsp(p);
729: while (*p != '\0')
730: {
731: s = tstr(&p, 0);
732: add_cmd_str(s);
733: }
734: add_cmd_char('\0');
735: }
736:
737: /*
738: * Parse a line from the lesskey file.
739: */
740: void
741: parse_line(line)
742: char *line;
743: {
744: char *p;
745:
746: /*
747: * See if it is a control line.
748: */
749: if (control_line(line))
750: return;
751: /*
752: * Skip leading white space.
753: * Replace the final newline with a null byte.
754: * Ignore blank lines and comments.
755: */
756: p = clean_line(line);
757: if (*p == '\0')
758: return;
759:
760: if (currtable == &vartable)
761: parse_varline(p);
762: else
763: parse_cmdline(p);
764: }
765:
766: int
1.1 etheisen 767: main(argc, argv)
768: int argc;
769: char *argv[];
770: {
771: FILE *desc;
772: FILE *out;
1.1.1.2 millert 773: char line[1024];
774:
775: #ifdef WIN32
776: if (getenv("HOME") == NULL)
777: {
778: /*
779: * If there is no HOME environment variable,
780: * try the concatenation of HOMEDRIVE + HOMEPATH.
781: */
782: char *drive = getenv("HOMEDRIVE");
783: char *path = getenv("HOMEPATH");
784: if (drive != NULL && path != NULL)
785: {
786: char *env = (char *) calloc(strlen(drive) +
787: strlen(path) + 6, sizeof(char));
788: strcpy(env, "HOME=");
789: strcat(env, drive);
790: strcat(env, path);
791: putenv(env);
792: }
793: }
794: #endif /* WIN32 */
1.1 etheisen 795:
796: /*
797: * Process command line arguments.
798: */
799: parse_args(argc, argv);
800: init_tables();
801:
802: /*
803: * Open the input file.
804: */
805: if (strcmp(infile, "-") == 0)
806: desc = stdin;
807: else if ((desc = fopen(infile, "r")) == NULL)
808: {
1.1.1.2 millert 809: #if HAVE_PERROR
1.1 etheisen 810: perror(infile);
1.1.1.2 millert 811: #else
812: fprintf(stderr, "Cannot open %s\n", infile);
813: #endif
814: usage();
1.1 etheisen 815: }
816:
817: /*
818: * Read and parse the input file, one line at a time.
819: */
820: errors = 0;
821: linenum = 0;
822: while (fgets(line, sizeof(line), desc) != NULL)
823: {
824: ++linenum;
825: parse_line(line);
826: }
827:
828: /*
829: * Write the output file.
830: * If no output file was specified, use "$HOME/.less"
831: */
832: if (errors > 0)
833: {
834: fprintf(stderr, "%d errors; no output produced\n", errors);
835: exit(1);
836: }
837:
838: if (outfile == NULL)
1.1.1.2 millert 839: outfile = getenv("LESSKEY");
840: if (outfile == NULL)
1.1 etheisen 841: outfile = homefile(LESSKEYFILE);
842: if ((out = fopen(outfile, "wb")) == NULL)
843: {
1.1.1.2 millert 844: #if HAVE_PERROR
1.1 etheisen 845: perror(outfile);
1.1.1.2 millert 846: #else
847: fprintf(stderr, "Cannot open %s\n", outfile);
848: #endif
1.1 etheisen 849: exit(1);
850: }
851:
852: /* File header */
853: fputbytes(out, fileheader, sizeof(fileheader));
854:
855: /* Command key section */
856: fputbytes(out, cmdsection, sizeof(cmdsection));
857: fputint(out, cmdtable.pbuffer - cmdtable.buffer);
858: fputbytes(out, (char *)cmdtable.buffer, cmdtable.pbuffer-cmdtable.buffer);
859: /* Edit key section */
860: fputbytes(out, editsection, sizeof(editsection));
861: fputint(out, edittable.pbuffer - edittable.buffer);
862: fputbytes(out, (char *)edittable.buffer, edittable.pbuffer-edittable.buffer);
863:
1.1.1.2 millert 864: /* Environment variable section */
865: fputbytes(out, varsection, sizeof(varsection));
866: fputint(out, vartable.pbuffer - vartable.buffer);
867: fputbytes(out, (char *)vartable.buffer, vartable.pbuffer-vartable.buffer);
868:
1.1 etheisen 869: /* File trailer */
870: fputbytes(out, endsection, sizeof(endsection));
871: fputbytes(out, filetrailer, sizeof(filetrailer));
1.1.1.2 millert 872: return (0);
1.1 etheisen 873: }