home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / less-321-src.tgz / tar.out / fsf / less / lesskey.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  16KB  |  761 lines

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