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