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