[BACK]Return to lesskey.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / less

Annotation of src/usr.bin/less/lesskey.c, Revision 1.1

1.1     ! etheisen    1: /*
        !             2:  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
        !             3:  * All rights reserved.
        !             4:  *
        !             5:  * Redistribution and use in source and binary forms, with or without
        !             6:  * modification, are permitted provided that the following conditions
        !             7:  * are met:
        !             8:  * 1. Redistributions of source code must retain the above copyright
        !             9:  *    notice, this list of conditions and the following disclaimer.
        !            10:  * 2. Redistributions in binary form must reproduce the above copyright
        !            11:  *    notice in the documentation and/or other materials provided with
        !            12:  *    the distribution.
        !            13:  *
        !            14:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
        !            15:  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        !            16:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
        !            17:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
        !            18:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
        !            19:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
        !            20:  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
        !            21:  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
        !            22:  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
        !            23:  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
        !            24:  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
        !            25:  */
        !            26:
        !            27:
        !            28: /*
        !            29:  *     lesskey [-o output] [input]
        !            30:  *
        !            31:  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
        !            32:  *
        !            33:  *     Make a .less file.
        !            34:  *     If no input file is specified, standard input is used.
        !            35:  *     If no output file is specified, $HOME/.less is used.
        !            36:  *
        !            37:  *     The .less file is used to specify (to "less") user-defined
        !            38:  *     key bindings.  Basically any sequence of 1 to MAX_CMDLEN
        !            39:  *     keystrokes may be bound to an existing less function.
        !            40:  *
        !            41:  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
        !            42:  *
        !            43:  *     The input file is an ascii file consisting of a
        !            44:  *     sequence of lines of the form:
        !            45:  *             string <whitespace> action [chars] <newline>
        !            46:  *
        !            47:  *     "string" is a sequence of command characters which form
        !            48:  *             the new user-defined command.  The command
        !            49:  *             characters may be:
        !            50:  *             1. The actual character itself.
        !            51:  *             2. A character preceded by ^ to specify a
        !            52:  *                control character (e.g. ^X means control-X).
        !            53:  *             3. A backslash followed by one to three octal digits
        !            54:  *                to specify a character by its octal value.
        !            55:  *             4. A backslash followed by b, e, n, r or t
        !            56:  *                to specify \b, ESC, \n, \r or \t, respectively.
        !            57:  *             5. Any character (other than those mentioned above) preceded
        !            58:  *                by a \ to specify the character itself (characters which
        !            59:  *                must be preceded by \ include ^, \, and whitespace.
        !            60:  *     "action" is the name of a "less" action, from the table below.
        !            61:  *     "chars" is an optional sequence of characters which is treated
        !            62:  *             as keyboard input after the command is executed.
        !            63:  *
        !            64:  *     Blank lines and lines which start with # are ignored,
        !            65:  *     except for the special control lines:
        !            66:  *             #line-edit      Signals the beginning of the line-editing
        !            67:  *                             keys section.
        !            68:  *             #stop           Stops command parsing in less;
        !            69:  *                             causes all default keys to be disabled.
        !            70:  *
        !            71:  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
        !            72:  *
        !            73:  *     The output file is a non-ascii file, consisting of a header,
        !            74:  *     one or more sections, and a trailer.
        !            75:  *     Each section begins with a section header, a section length word
        !            76:  *     and the section data.  Normally there are three sections:
        !            77:  *             CMD_SECTION     Definition of command keys.
        !            78:  *             EDIT_SECTION    Definition of editing keys.
        !            79:  *             END_SECTION     A special section header, with no
        !            80:  *                             length word or section data.
        !            81:  *
        !            82:  *     Section data consists of zero or more byte sequences of the form:
        !            83:  *             string <0> <action>
        !            84:  *     or
        !            85:  *             string <0> <action|A_EXTRA> chars <0>
        !            86:  *
        !            87:  *     "string" is the command string.
        !            88:  *     "<0>" is one null byte.
        !            89:  *     "<action>" is one byte containing the action code (the A_xxx value).
        !            90:  *     If action is ORed with A_EXTRA, the action byte is followed
        !            91:  *             by the null-terminated "chars" string.
        !            92:  *
        !            93:  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
        !            94:  */
        !            95:
        !            96: #include "less.h"
        !            97: #include "lesskey.h"
        !            98: #include "cmd.h"
        !            99:
        !           100: struct cmdname
        !           101: {
        !           102:        char *cn_name;
        !           103:        int cn_action;
        !           104: };
        !           105:
        !           106: struct cmdname cmdnames[] =
        !           107: {
        !           108:        "back-bracket",         A_B_BRACKET,
        !           109:        "back-line",            A_B_LINE,
        !           110:        "back-line-force",      A_BF_LINE,
        !           111:        "back-screen",          A_B_SCREEN,
        !           112:        "back-scroll",          A_B_SCROLL,
        !           113:        "back-search",          A_B_SEARCH,
        !           114:        "back-window",          A_B_WINDOW,
        !           115:        "debug",                A_DEBUG,
        !           116:        "display-flag",         A_DISP_OPTION,
        !           117:        "display-option",       A_DISP_OPTION,
        !           118:        "end",                  A_GOEND,
        !           119:        "examine",              A_EXAMINE,
        !           120:        "first-cmd",            A_FIRSTCMD,
        !           121:        "firstcmd",             A_FIRSTCMD,
        !           122:        "flush-repaint",        A_FREPAINT,
        !           123:        "forw-bracket",         A_F_BRACKET,
        !           124:        "forw-forever",         A_F_FOREVER,
        !           125:        "forw-line",            A_F_LINE,
        !           126:        "forw-line-force",      A_FF_LINE,
        !           127:        "forw-screen",          A_F_SCREEN,
        !           128:        "forw-scroll",          A_F_SCROLL,
        !           129:        "forw-search",          A_F_SEARCH,
        !           130:        "forw-window",          A_F_WINDOW,
        !           131:        "goto-end",             A_GOEND,
        !           132:        "goto-line",            A_GOLINE,
        !           133:        "goto-mark",            A_GOMARK,
        !           134:        "help",                 A_HELP,
        !           135:        "index-file",           A_INDEX_FILE,
        !           136:        "invalid",              A_UINVALID,
        !           137:        "next-file",            A_NEXT_FILE,
        !           138:        "noaction",             A_NOACTION,
        !           139:        "percent",              A_PERCENT,
        !           140:        "pipe",                 A_PIPE,
        !           141:        "prev-file",            A_PREV_FILE,
        !           142:        "quit",                 A_QUIT,
        !           143:        "repaint",              A_REPAINT,
        !           144:        "repaint-flush",        A_FREPAINT,
        !           145:        "repeat-search",        A_AGAIN_SEARCH,
        !           146:        "repeat-search-all",    A_T_AGAIN_SEARCH,
        !           147:        "reverse-search",       A_REVERSE_SEARCH,
        !           148:        "reverse-search-all",   A_T_REVERSE_SEARCH,
        !           149:        "set-mark",             A_SETMARK,
        !           150:        "shell",                A_SHELL,
        !           151:        "status",               A_STAT,
        !           152:        "toggle-flag",          A_OPT_TOGGLE,
        !           153:        "toggle-option",        A_OPT_TOGGLE,
        !           154:        "undo-hilite",          A_UNDO_SEARCH,
        !           155:        "version",              A_VERSION,
        !           156:        "visual",               A_VISUAL,
        !           157:        NULL,                   0
        !           158: };
        !           159:
        !           160: struct cmdname editnames[] =
        !           161: {
        !           162:        "back-complete",        EC_B_COMPLETE,
        !           163:        "backspace",            EC_BACKSPACE,
        !           164:        "delete",               EC_DELETE,
        !           165:        "down",                 EC_DOWN,
        !           166:        "end",                  EC_END,
        !           167:        "expand",               EC_EXPAND,
        !           168:        "forw-complete",        EC_F_COMPLETE,
        !           169:        "home",                 EC_HOME,
        !           170:        "insert",               EC_INSERT,
        !           171:        "invalid",              EC_UINVALID,
        !           172:        "kill-line",            EC_LINEKILL,
        !           173:        "left",                 EC_LEFT,
        !           174:        "literal",              EC_LITERAL,
        !           175:        "right",                EC_RIGHT,
        !           176:        "up",                   EC_UP,
        !           177:        "word-backspace",       EC_W_BACKSPACE,
        !           178:        "word-delete",          EC_W_DELETE,
        !           179:        "word-left",            EC_W_RIGHT,
        !           180:        "word-right",           EC_W_LEFT,
        !           181:        NULL,                   0
        !           182: };
        !           183:
        !           184: struct table
        !           185: {
        !           186:        struct cmdname *names;
        !           187:        char *pbuffer;
        !           188:        char buffer[MAX_USERCMD];
        !           189: };
        !           190:
        !           191: struct table cmdtable;
        !           192: struct table edittable;
        !           193: struct table *currtable = &cmdtable;
        !           194:
        !           195: char fileheader[] = {
        !           196:        C0_LESSKEY_MAGIC,
        !           197:        C1_LESSKEY_MAGIC,
        !           198:        C2_LESSKEY_MAGIC,
        !           199:        C3_LESSKEY_MAGIC
        !           200: };
        !           201: char filetrailer[] = {
        !           202:        C0_END_LESSKEY_MAGIC,
        !           203:        C1_END_LESSKEY_MAGIC,
        !           204:        C2_END_LESSKEY_MAGIC
        !           205: };
        !           206: char cmdsection[1] =   { CMD_SECTION };
        !           207: char editsection[1] =  { EDIT_SECTION };
        !           208: char endsection[1] =   { END_SECTION };
        !           209:
        !           210: char *infile = NULL;
        !           211: char *outfile = NULL ;
        !           212:
        !           213: int linenum;
        !           214: int errors;
        !           215:
        !           216: extern char version[];
        !           217:
        !           218:        char *
        !           219: mkpathname(dirname, filename)
        !           220:        char *dirname;
        !           221:        char *filename;
        !           222: {
        !           223:        char *pathname;
        !           224:
        !           225:        pathname = calloc(strlen(dirname) + strlen(filename) + 2, sizeof(char));
        !           226:        strcpy(pathname, dirname);
        !           227: #if MSOFTC || OS2
        !           228:        strcat(pathname, "\\");
        !           229: #else
        !           230:        strcat(pathname, "/");
        !           231: #endif
        !           232:        strcat(pathname, filename);
        !           233:        return (pathname);
        !           234: }
        !           235:
        !           236: /*
        !           237:  * Figure out the name of a default file (in the user's HOME directory).
        !           238:  */
        !           239:        char *
        !           240: homefile(filename)
        !           241:        char *filename;
        !           242: {
        !           243:        char *p;
        !           244:        char *pathname;
        !           245:
        !           246:        if ((p = getenv("HOME")) != NULL && *p != '\0')
        !           247:                pathname = mkpathname(p, filename);
        !           248: #if OS2
        !           249:        else if ((p = getenv("INIT")) != NULL && *p != '\0')
        !           250:                pathname = mkpathname(p, filename);
        !           251: #endif
        !           252:        else
        !           253:        {
        !           254:                fprintf(stderr, "cannot find $HOME - using current directory\n");
        !           255:                pathname = mkpathname(".", filename);
        !           256:        }
        !           257:        return (pathname);
        !           258: }
        !           259:
        !           260: /*
        !           261:  * Parse command line arguments.
        !           262:  */
        !           263:        void
        !           264: parse_args(argc, argv)
        !           265:        int argc;
        !           266:        char **argv;
        !           267: {
        !           268:        outfile = NULL;
        !           269:        while (--argc > 0 && **(++argv) == '-' && argv[0][1] != '\0')
        !           270:        {
        !           271:                switch (argv[0][1])
        !           272:                {
        !           273:                case 'o':
        !           274:                        outfile = &argv[0][2];
        !           275:                        if (*outfile == '\0')
        !           276:                        {
        !           277:                                if (--argc <= 0)
        !           278:                                        usage();
        !           279:                                outfile = *(++argv);
        !           280:                        }
        !           281:                        break;
        !           282:                case 'V':
        !           283:                        printf("lesskey  version %s\n", version);
        !           284:                        exit(0);
        !           285:                default:
        !           286:                        usage();
        !           287:                }
        !           288:        }
        !           289:        if (argc > 1)
        !           290:                usage();
        !           291:        /*
        !           292:         * Open the input file, or use DEF_LESSKEYINFILE if none specified.
        !           293:         */
        !           294:        if (argc > 0)
        !           295:                infile = *argv;
        !           296:        else
        !           297:                infile = homefile(DEF_LESSKEYINFILE);
        !           298: }
        !           299:
        !           300: /*
        !           301:  * Initialize data structures.
        !           302:  */
        !           303:        void
        !           304: init_tables()
        !           305: {
        !           306:        cmdtable.names = cmdnames;
        !           307:        cmdtable.pbuffer = cmdtable.buffer;
        !           308:
        !           309:        edittable.names = editnames;
        !           310:        edittable.pbuffer = edittable.buffer;
        !           311: }
        !           312:
        !           313: /*
        !           314:  * Parse one character of a string.
        !           315:  */
        !           316:        int
        !           317: tchar(pp)
        !           318:        char **pp;
        !           319: {
        !           320:        register char *p;
        !           321:        register char ch;
        !           322:        register int i;
        !           323:
        !           324:        p = *pp;
        !           325:        switch (*p)
        !           326:        {
        !           327:        case '\\':
        !           328:                ++p;
        !           329:                switch (*p)
        !           330:                {
        !           331:                case '0': case '1': case '2': case '3':
        !           332:                case '4': case '5': case '6': case '7':
        !           333:                        /*
        !           334:                         * Parse an octal number.
        !           335:                         */
        !           336:                        ch = 0;
        !           337:                        i = 0;
        !           338:                        do
        !           339:                                ch = 8*ch + (*p - '0');
        !           340:                        while (*++p >= '0' && *p <= '7' && ++i < 3);
        !           341:                        *pp = p;
        !           342:                        return (ch);
        !           343:                case 'b':
        !           344:                        *pp = p+1;
        !           345:                        return ('\r');
        !           346:                case 'e':
        !           347:                        *pp = p+1;
        !           348:                        return (ESC);
        !           349:                case 'n':
        !           350:                        *pp = p+1;
        !           351:                        return ('\n');
        !           352:                case 'r':
        !           353:                        *pp = p+1;
        !           354:                        return ('\r');
        !           355:                case 't':
        !           356:                        *pp = p+1;
        !           357:                        return ('\t');
        !           358:                default:
        !           359:                        /*
        !           360:                         * Backslash followed by any other char
        !           361:                         * just means that char.
        !           362:                         */
        !           363:                        *pp = p+1;
        !           364:                        return (*p);
        !           365:                }
        !           366:        case '^':
        !           367:                /*
        !           368:                 * Carat means CONTROL.
        !           369:                 */
        !           370:                *pp = p+2;
        !           371:                return (CONTROL(p[1]));
        !           372:        }
        !           373:        *pp = p+1;
        !           374:        return (*p);
        !           375: }
        !           376:
        !           377: /*
        !           378:  * Skip leading spaces in a string.
        !           379:  */
        !           380:        public char *
        !           381: skipsp(s)
        !           382:        register char *s;
        !           383: {
        !           384:        while (*s == ' ' || *s == '\t')
        !           385:                s++;
        !           386:        return (s);
        !           387: }
        !           388:
        !           389: /*
        !           390:  * Skip non-space characters in a string.
        !           391:  */
        !           392:        public char *
        !           393: skipnsp(s)
        !           394:        register char *s;
        !           395: {
        !           396:        while (*s != '\0' && *s != ' ' && *s != '\t')
        !           397:                s++;
        !           398:        return (s);
        !           399: }
        !           400:
        !           401: /*
        !           402:  * Clean up an input line:
        !           403:  * strip off the trailing newline & any trailing # comment.
        !           404:  */
        !           405:        char *
        !           406: clean_line(s)
        !           407:        char *s;
        !           408: {
        !           409:        register int i;
        !           410:
        !           411:        s = skipsp(s);
        !           412:        for (i = 0;  s[i] != '\n' && s[i] != '\0';  i++)
        !           413:                if (s[i] == '#' && (i == 0 || s[i-1] != '\\'))
        !           414:                        break;
        !           415:        s[i] = '\0';
        !           416:        return (s);
        !           417: }
        !           418:
        !           419: /*
        !           420:  * Add a byte to the output command table.
        !           421:  */
        !           422:        void
        !           423: add_cmd_char(c)
        !           424:        int c;
        !           425: {
        !           426:        if (currtable->pbuffer >= currtable->buffer + MAX_USERCMD)
        !           427:        {
        !           428:                error("too many commands");
        !           429:                exit(1);
        !           430:        }
        !           431:        *(currtable->pbuffer)++ = c;
        !           432: }
        !           433:
        !           434: /*
        !           435:  * See if we have a special "control" line.
        !           436:  */
        !           437:        int
        !           438: control_line(s)
        !           439:        char *s;
        !           440: {
        !           441: #define        PREFIX(str,pat) (strncmp(str,pat,strlen(pat)-1) == 0)
        !           442:
        !           443:        if (PREFIX(s, "#line-edit"))
        !           444:        {
        !           445:                currtable = &edittable;
        !           446:                return (1);
        !           447:        }
        !           448:        if (PREFIX(s, "#command"))
        !           449:        {
        !           450:                currtable = &cmdtable;
        !           451:                return (1);
        !           452:        }
        !           453:        if (PREFIX(s, "#stop"))
        !           454:        {
        !           455:                add_cmd_char('\0');
        !           456:                add_cmd_char(A_END_LIST);
        !           457:                return (1);
        !           458:        }
        !           459:        return (0);
        !           460: }
        !           461:
        !           462: /*
        !           463:  * Output some bytes.
        !           464:  */
        !           465:        void
        !           466: fputbytes(fd, buf, len)
        !           467:        FILE *fd;
        !           468:        char *buf;
        !           469:        int len;
        !           470: {
        !           471:        while (len-- > 0)
        !           472:        {
        !           473:                fwrite(buf, sizeof(char), 1, fd);
        !           474:                buf++;
        !           475:        }
        !           476: }
        !           477:
        !           478: /*
        !           479:  * Output an integer, in special KRADIX form.
        !           480:  */
        !           481:        void
        !           482: fputint(fd, val)
        !           483:        FILE *fd;
        !           484:        unsigned int val;
        !           485: {
        !           486:        char c;
        !           487:
        !           488:        if (val >= KRADIX*KRADIX)
        !           489:        {
        !           490:                fprintf(stderr, "error: integer too big (%d > %d)\n",
        !           491:                        val, KRADIX*KRADIX);
        !           492:                exit(1);
        !           493:        }
        !           494:        c = val % KRADIX;
        !           495:        fwrite(&c, sizeof(char), 1, fd);
        !           496:        c = val / KRADIX;
        !           497:        fwrite(&c, sizeof(char), 1, fd);
        !           498: }
        !           499:
        !           500: /*
        !           501:  * Find an action, given the name of the action.
        !           502:  */
        !           503:        int
        !           504: findaction(actname)
        !           505:        char *actname;
        !           506: {
        !           507:        int i;
        !           508:
        !           509:        for (i = 0;  currtable->names[i].cn_name != NULL;  i++)
        !           510:                if (strcmp(currtable->names[i].cn_name, actname) == 0)
        !           511:                        return (currtable->names[i].cn_action);
        !           512:        error("unknown action");
        !           513:        return (A_INVALID);
        !           514: }
        !           515:
        !           516: usage()
        !           517: {
        !           518:        fprintf(stderr, "usage: lesskey [-o output] [input]\n");
        !           519:        exit(1);
        !           520: }
        !           521:
        !           522:        void
        !           523: error(s)
        !           524:        char *s;
        !           525: {
        !           526:        fprintf(stderr, "line %d: %s\n", linenum, s);
        !           527:        errors++;
        !           528: }
        !           529:
        !           530:
        !           531: /*
        !           532:  * Parse a line from the lesskey file.
        !           533:  */
        !           534:        void
        !           535: parse_line(line)
        !           536:        char *line;
        !           537: {
        !           538:        char *p;
        !           539:        int cmdlen;
        !           540:        char *actname;
        !           541:        int action;
        !           542:        int c;
        !           543:
        !           544:        /*
        !           545:         * See if it is a control line.
        !           546:         */
        !           547:        if (control_line(line))
        !           548:                return;
        !           549:        /*
        !           550:         * Skip leading white space.
        !           551:         * Replace the final newline with a null byte.
        !           552:         * Ignore blank lines and comments.
        !           553:         */
        !           554:        p = clean_line(line);
        !           555:        if (*p == '\0')
        !           556:                return;
        !           557:
        !           558:        /*
        !           559:         * Parse the command string and store it in the current table.
        !           560:         */
        !           561:        cmdlen = 0;
        !           562:        do
        !           563:        {
        !           564:                c = tchar(&p);
        !           565:                if (++cmdlen > MAX_CMDLEN)
        !           566:                        error("command too long");
        !           567:                else
        !           568:                        add_cmd_char(c);
        !           569:        } while (*p != ' ' && *p != '\t' && *p != '\0');
        !           570:        /*
        !           571:         * Terminate the command string with a null byte.
        !           572:         */
        !           573:        add_cmd_char('\0');
        !           574:
        !           575:        /*
        !           576:         * Skip white space between the command string
        !           577:         * and the action name.
        !           578:         * Terminate the action name with a null byte.
        !           579:         */
        !           580:        p = skipsp(p);
        !           581:        if (*p == '\0')
        !           582:        {
        !           583:                error("missing action");
        !           584:                return;
        !           585:        }
        !           586:        actname = p;
        !           587:        p = skipnsp(p);
        !           588:        c = *p;
        !           589:        *p = '\0';
        !           590:
        !           591:        /*
        !           592:         * Parse the action name and store it in the current table.
        !           593:         */
        !           594:        action = findaction(actname);
        !           595:
        !           596:        /*
        !           597:         * See if an extra string follows the action name.
        !           598:         */
        !           599:        *p = c;
        !           600:        p = skipsp(p);
        !           601:        if (*p == '\0')
        !           602:        {
        !           603:                add_cmd_char(action);
        !           604:        } else
        !           605:        {
        !           606:                /*
        !           607:                 * OR the special value A_EXTRA into the action byte.
        !           608:                 * Put the extra string after the action byte.
        !           609:                 */
        !           610:                add_cmd_char(action | A_EXTRA);
        !           611:                while (*p != '\0')
        !           612:                        add_cmd_char(tchar(&p));
        !           613:                add_cmd_char('\0');
        !           614:        }
        !           615: }
        !           616:
        !           617: main(argc, argv)
        !           618:        int argc;
        !           619:        char *argv[];
        !           620: {
        !           621:        FILE *desc;
        !           622:        FILE *out;
        !           623:        char line[200];
        !           624:
        !           625:        /*
        !           626:         * Process command line arguments.
        !           627:         */
        !           628:        parse_args(argc, argv);
        !           629:        init_tables();
        !           630:
        !           631:        /*
        !           632:         * Open the input file.
        !           633:         */
        !           634:        if (strcmp(infile, "-") == 0)
        !           635:                desc = stdin;
        !           636:        else if ((desc = fopen(infile, "r")) == NULL)
        !           637:        {
        !           638:                perror(infile);
        !           639:                exit(1);
        !           640:        }
        !           641:
        !           642:        /*
        !           643:         * Read and parse the input file, one line at a time.
        !           644:         */
        !           645:        errors = 0;
        !           646:        linenum = 0;
        !           647:        while (fgets(line, sizeof(line), desc) != NULL)
        !           648:        {
        !           649:                ++linenum;
        !           650:                parse_line(line);
        !           651:        }
        !           652:
        !           653:        /*
        !           654:         * Write the output file.
        !           655:         * If no output file was specified, use "$HOME/.less"
        !           656:         */
        !           657:        if (errors > 0)
        !           658:        {
        !           659:                fprintf(stderr, "%d errors; no output produced\n", errors);
        !           660:                exit(1);
        !           661:        }
        !           662:
        !           663:        if (outfile == NULL)
        !           664:                outfile = homefile(LESSKEYFILE);
        !           665:        if ((out = fopen(outfile, "wb")) == NULL)
        !           666:        {
        !           667:                perror(outfile);
        !           668:                exit(1);
        !           669:        }
        !           670:
        !           671:        /* File header */
        !           672:        fputbytes(out, fileheader, sizeof(fileheader));
        !           673:
        !           674:        /* Command key section */
        !           675:        fputbytes(out, cmdsection, sizeof(cmdsection));
        !           676:        fputint(out, cmdtable.pbuffer - cmdtable.buffer);
        !           677:        fputbytes(out, (char *)cmdtable.buffer, cmdtable.pbuffer-cmdtable.buffer);
        !           678:        /* Edit key section */
        !           679:        fputbytes(out, editsection, sizeof(editsection));
        !           680:        fputint(out, edittable.pbuffer - edittable.buffer);
        !           681:        fputbytes(out, (char *)edittable.buffer, edittable.pbuffer-edittable.buffer);
        !           682:
        !           683:        /* File trailer */
        !           684:        fputbytes(out, endsection, sizeof(endsection));
        !           685:        fputbytes(out, filetrailer, sizeof(filetrailer));
        !           686:        exit(0);
        !           687: }