[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

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: }