Import less-436.
[dragonfly.git] / contrib / less / lesskey.c
1 /*
2  * Copyright (C) 1984-2009  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         { "filter",               A_FILTER },
110         { "first-cmd",            A_FIRSTCMD },
111         { "firstcmd",             A_FIRSTCMD },
112         { "flush-repaint",        A_FREPAINT },
113         { "forw-bracket",         A_F_BRACKET },
114         { "forw-forever",         A_F_FOREVER },
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         { "left",               EC_LEFT },
170         { "literal",            EC_LITERAL },
171         { "right",              EC_RIGHT },
172         { "up",                 EC_UP },
173         { "word-backspace",     EC_W_BACKSPACE },
174         { "word-delete",        EC_W_DELETE },
175         { "word-left",          EC_W_LEFT },
176         { "word-right",         EC_W_RIGHT },
177         { NULL, 0 }
178 };
179
180 struct table
181 {
182         struct cmdname *names;
183         char *pbuffer;
184         char buffer[MAX_USERCMD];
185 };
186
187 struct table cmdtable;
188 struct table edittable;
189 struct table vartable;
190 struct table *currtable = &cmdtable;
191
192 char fileheader[] = {
193         C0_LESSKEY_MAGIC, 
194         C1_LESSKEY_MAGIC, 
195         C2_LESSKEY_MAGIC, 
196         C3_LESSKEY_MAGIC
197 };
198 char filetrailer[] = {
199         C0_END_LESSKEY_MAGIC, 
200         C1_END_LESSKEY_MAGIC, 
201         C2_END_LESSKEY_MAGIC
202 };
203 char cmdsection[1] =    { CMD_SECTION };
204 char editsection[1] =   { EDIT_SECTION };
205 char varsection[1] =    { VAR_SECTION };
206 char endsection[1] =    { END_SECTION };
207
208 char *infile = NULL;
209 char *outfile = NULL ;
210
211 int linenum;
212 int errors;
213
214 extern char version[];
215
216         void
217 usage()
218 {
219         fprintf(stderr, "usage: lesskey [-o output] [input]\n");
220         exit(1);
221 }
222
223         char *
224 mkpathname(dirname, filename)
225         char *dirname;
226         char *filename;
227 {
228         char *pathname;
229
230         pathname = calloc(strlen(dirname) + strlen(filename) + 2, sizeof(char));
231         strcpy(pathname, dirname);
232         strcat(pathname, PATHNAME_SEP);
233         strcat(pathname, filename);
234         return (pathname);
235 }
236
237 /*
238  * Figure out the name of a default file (in the user's HOME directory).
239  */
240         char *
241 homefile(filename)
242         char *filename;
243 {
244         char *p;
245         char *pathname;
246
247         if ((p = getenv("HOME")) != NULL && *p != '\0')
248                 pathname = mkpathname(p, filename);
249 #if OS2
250         else if ((p = getenv("INIT")) != NULL && *p != '\0')
251                 pathname = mkpathname(p, filename);
252 #endif
253         else
254         {
255                 fprintf(stderr, "cannot find $HOME - using current directory\n");
256                 pathname = mkpathname(".", filename);
257         }
258         return (pathname);
259 }
260
261 /*
262  * Parse command line arguments.
263  */
264         void
265 parse_args(argc, argv)
266         int argc;
267         char **argv;
268 {
269         char *arg;
270
271         outfile = NULL;
272         while (--argc > 0)
273         {
274                 arg = *++argv;
275                 if (arg[0] != '-')
276                         /* Arg does not start with "-"; it's not an option. */
277                         break;
278                 if (arg[1] == '\0')
279                         /* "-" means standard input. */
280                         break;
281                 if (arg[1] == '-' && arg[2] == '\0')
282                 {
283                         /* "--" means end of options. */
284                         argc--;
285                         argv++;
286                         break;
287                 }
288                 switch (arg[1])
289                 {
290                 case '-':
291                         if (strncmp(arg, "--output", 8) == 0)
292                         {
293                                 if (arg[8] == '\0')
294                                         outfile = &arg[8];
295                                 else if (arg[8] == '=')
296                                         outfile = &arg[9];
297                                 else
298                                         usage();
299                                 goto opt_o;
300                         }
301                         if (strcmp(arg, "--version") == 0)
302                         {
303                                 goto opt_V;
304                         }
305                         usage();
306                         break;
307                 case 'o':
308                         outfile = &argv[0][2];
309                 opt_o:
310                         if (*outfile == '\0')
311                         {
312                                 if (--argc <= 0)
313                                         usage();
314                                 outfile = *(++argv);
315                         }
316                         break;
317                 case 'V':
318                 opt_V:
319                         printf("lesskey  version %s\n", version);
320                         exit(0);
321                 default:
322                         usage();
323                 }
324         }
325         if (argc > 1)
326                 usage();
327         /*
328          * Open the input file, or use DEF_LESSKEYINFILE if none specified.
329          */
330         if (argc > 0)
331                 infile = *argv;
332         else
333                 infile = homefile(DEF_LESSKEYINFILE);
334 }
335
336 /*
337  * Initialize data structures.
338  */
339         void
340 init_tables()
341 {
342         cmdtable.names = cmdnames;
343         cmdtable.pbuffer = cmdtable.buffer;
344
345         edittable.names = editnames;
346         edittable.pbuffer = edittable.buffer;
347
348         vartable.names = NULL;
349         vartable.pbuffer = vartable.buffer;
350 }
351
352 /*
353  * Parse one character of a string.
354  */
355         char *
356 tstr(pp, xlate)
357         char **pp;
358         int xlate;
359 {
360         register char *p;
361         register char ch;
362         register int i;
363         static char buf[10];
364         static char tstr_control_k[] =
365                 { SK_SPECIAL_KEY, SK_CONTROL_K, 6, 1, 1, 1, '\0' };
366
367         p = *pp;
368         switch (*p)
369         {
370         case '\\':
371                 ++p;
372                 switch (*p)
373                 {
374                 case '0': case '1': case '2': case '3':
375                 case '4': case '5': case '6': case '7':
376                         /*
377                          * Parse an octal number.
378                          */
379                         ch = 0;
380                         i = 0;
381                         do
382                                 ch = 8*ch + (*p - '0');
383                         while (*++p >= '0' && *p <= '7' && ++i < 3);
384                         *pp = p;
385                         if (xlate && ch == CONTROL('K'))
386                                 return tstr_control_k;
387                         buf[0] = ch;
388                         buf[1] = '\0';
389                         return (buf);
390                 case 'b':
391                         *pp = p+1;
392                         return ("\b");
393                 case 'e':
394                         *pp = p+1;
395                         buf[0] = ESC;
396                         buf[1] = '\0';
397                         return (buf);
398                 case 'n':
399                         *pp = p+1;
400                         return ("\n");
401                 case 'r':
402                         *pp = p+1;
403                         return ("\r");
404                 case 't':
405                         *pp = p+1;
406                         return ("\t");
407                 case 'k':
408                         if (xlate)
409                         {
410                                 switch (*++p)
411                                 {
412                                 case 'u': ch = SK_UP_ARROW; break;
413                                 case 'd': ch = SK_DOWN_ARROW; break;
414                                 case 'r': ch = SK_RIGHT_ARROW; break;
415                                 case 'l': ch = SK_LEFT_ARROW; break;
416                                 case 'U': ch = SK_PAGE_UP; break;
417                                 case 'D': ch = SK_PAGE_DOWN; break;
418                                 case 'h': ch = SK_HOME; break;
419                                 case 'e': ch = SK_END; break;
420                                 case 'x': ch = SK_DELETE; break;
421                                 default:
422                                         error("illegal char after \\k");
423                                         *pp = p+1;
424                                         return ("");
425                                 }
426                                 *pp = p+1;
427                                 buf[0] = SK_SPECIAL_KEY;
428                                 buf[1] = ch;
429                                 buf[2] = 6;
430                                 buf[3] = 1;
431                                 buf[4] = 1;
432                                 buf[5] = 1;
433                                 buf[6] = '\0';
434                                 return (buf);
435                         }
436                         /* FALLTHRU */
437                 default:
438                         /*
439                          * Backslash followed by any other char 
440                          * just means that char.
441                          */
442                         *pp = p+1;
443                         buf[0] = *p;
444                         buf[1] = '\0';
445                         if (xlate && buf[0] == CONTROL('K'))
446                                 return tstr_control_k;
447                         return (buf);
448                 }
449         case '^':
450                 /*
451                  * Carat means CONTROL.
452                  */
453                 *pp = p+2;
454                 buf[0] = CONTROL(p[1]);
455                 buf[1] = '\0';
456                 if (buf[0] == CONTROL('K'))
457                         return tstr_control_k;
458                 return (buf);
459         }
460         *pp = p+1;
461         buf[0] = *p;
462         buf[1] = '\0';
463         if (xlate && buf[0] == CONTROL('K'))
464                 return tstr_control_k;
465         return (buf);
466 }
467
468 /*
469  * Skip leading spaces in a string.
470  */
471         public char *
472 skipsp(s)
473         register char *s;
474 {
475         while (*s == ' ' || *s == '\t') 
476                 s++;
477         return (s);
478 }
479
480 /*
481  * Skip non-space characters in a string.
482  */
483         public char *
484 skipnsp(s)
485         register char *s;
486 {
487         while (*s != '\0' && *s != ' ' && *s != '\t')
488                 s++;
489         return (s);
490 }
491
492 /*
493  * Clean up an input line:
494  * strip off the trailing newline & any trailing # comment.
495  */
496         char *
497 clean_line(s)
498         char *s;
499 {
500         register int i;
501
502         s = skipsp(s);
503         for (i = 0;  s[i] != '\n' && s[i] != '\r' && s[i] != '\0';  i++)
504                 if (s[i] == '#' && (i == 0 || s[i-1] != '\\'))
505                         break;
506         s[i] = '\0';
507         return (s);
508 }
509
510 /*
511  * Add a byte to the output command table.
512  */
513         void
514 add_cmd_char(c)
515         int c;
516 {
517         if (currtable->pbuffer >= currtable->buffer + MAX_USERCMD)
518         {
519                 error("too many commands");
520                 exit(1);
521         }
522         *(currtable->pbuffer)++ = c;
523 }
524
525 /*
526  * Add a string to the output command table.
527  */
528         void
529 add_cmd_str(s)
530         char *s;
531 {
532         for ( ;  *s != '\0';  s++)
533                 add_cmd_char(*s);
534 }
535
536 /*
537  * See if we have a special "control" line.
538  */
539         int
540 control_line(s)
541         char *s;
542 {
543 #define PREFIX(str,pat) (strncmp(str,pat,strlen(pat)) == 0)
544
545         if (PREFIX(s, "#line-edit"))
546         {
547                 currtable = &edittable;
548                 return (1);
549         }
550         if (PREFIX(s, "#command"))
551         {
552                 currtable = &cmdtable;
553                 return (1);
554         }
555         if (PREFIX(s, "#env"))
556         {
557                 currtable = &vartable;
558                 return (1);
559         }
560         if (PREFIX(s, "#stop"))
561         {
562                 add_cmd_char('\0');
563                 add_cmd_char(A_END_LIST);
564                 return (1);
565         }
566         return (0);
567 }
568
569 /*
570  * Output some bytes.
571  */
572         void
573 fputbytes(fd, buf, len)
574         FILE *fd;
575         char *buf;
576         int len;
577 {
578         while (len-- > 0)
579         {
580                 fwrite(buf, sizeof(char), 1, fd);
581                 buf++;
582         }
583 }
584
585 /*
586  * Output an integer, in special KRADIX form.
587  */
588         void
589 fputint(fd, val)
590         FILE *fd;
591         unsigned int val;
592 {
593         char c;
594
595         if (val >= KRADIX*KRADIX)
596         {
597                 fprintf(stderr, "error: integer too big (%d > %d)\n", 
598                         val, KRADIX*KRADIX);
599                 exit(1);
600         }
601         c = val % KRADIX;
602         fwrite(&c, sizeof(char), 1, fd);
603         c = val / KRADIX;
604         fwrite(&c, sizeof(char), 1, fd);
605 }
606
607 /*
608  * Find an action, given the name of the action.
609  */
610         int
611 findaction(actname)
612         char *actname;
613 {
614         int i;
615
616         for (i = 0;  currtable->names[i].cn_name != NULL;  i++)
617                 if (strcmp(currtable->names[i].cn_name, actname) == 0)
618                         return (currtable->names[i].cn_action);
619         error("unknown action");
620         return (A_INVALID);
621 }
622
623         void
624 error(s)
625         char *s;
626 {
627         fprintf(stderr, "line %d: %s\n", linenum, s);
628         errors++;
629 }
630
631
632         void
633 parse_cmdline(p)
634         char *p;
635 {
636         int cmdlen;
637         char *actname;
638         int action;
639         char *s;
640         char c;
641
642         /*
643          * Parse the command string and store it in the current table.
644          */
645         cmdlen = 0;
646         do
647         {
648                 s = tstr(&p, 1);
649                 cmdlen += strlen(s);
650                 if (cmdlen > MAX_CMDLEN)
651                         error("command too long");
652                 else
653                         add_cmd_str(s);
654         } while (*p != ' ' && *p != '\t' && *p != '\0');
655         /*
656          * Terminate the command string with a null byte.
657          */
658         add_cmd_char('\0');
659
660         /*
661          * Skip white space between the command string
662          * and the action name.
663          * Terminate the action name with a null byte.
664          */
665         p = skipsp(p);
666         if (*p == '\0')
667         {
668                 error("missing action");
669                 return;
670         }
671         actname = p;
672         p = skipnsp(p);
673         c = *p;
674         *p = '\0';
675
676         /*
677          * Parse the action name and store it in the current table.
678          */
679         action = findaction(actname);
680
681         /*
682          * See if an extra string follows the action name.
683          */
684         *p = c;
685         p = skipsp(p);
686         if (*p == '\0')
687         {
688                 add_cmd_char(action);
689         } else
690         {
691                 /*
692                  * OR the special value A_EXTRA into the action byte.
693                  * Put the extra string after the action byte.
694                  */
695                 add_cmd_char(action | A_EXTRA);
696                 while (*p != '\0')
697                         add_cmd_str(tstr(&p, 0));
698                 add_cmd_char('\0');
699         }
700 }
701
702         void
703 parse_varline(p)
704         char *p;
705 {
706         char *s;
707
708         do
709         {
710                 s = tstr(&p, 0);
711                 add_cmd_str(s);
712         } while (*p != ' ' && *p != '\t' && *p != '=' && *p != '\0');
713         /*
714          * Terminate the variable name with a null byte.
715          */
716         add_cmd_char('\0');
717
718         p = skipsp(p);
719         if (*p++ != '=')
720         {
721                 error("missing =");
722                 return;
723         }
724
725         add_cmd_char(EV_OK|A_EXTRA);
726
727         p = skipsp(p);
728         while (*p != '\0')
729         {
730                 s = tstr(&p, 0);
731                 add_cmd_str(s);
732         }
733         add_cmd_char('\0');
734 }
735
736 /*
737  * Parse a line from the lesskey file.
738  */
739         void
740 parse_line(line)
741         char *line;
742 {
743         char *p;
744
745         /*
746          * See if it is a control line.
747          */
748         if (control_line(line))
749                 return;
750         /*
751          * Skip leading white space.
752          * Replace the final newline with a null byte.
753          * Ignore blank lines and comments.
754          */
755         p = clean_line(line);
756         if (*p == '\0')
757                 return;
758
759         if (currtable == &vartable)
760                 parse_varline(p);
761         else
762                 parse_cmdline(p);
763 }
764
765         int
766 main(argc, argv)
767         int argc;
768         char *argv[];
769 {
770         FILE *desc;
771         FILE *out;
772         char line[1024];
773
774 #ifdef WIN32
775         if (getenv("HOME") == NULL)
776         {
777                 /*
778                  * If there is no HOME environment variable,
779                  * try the concatenation of HOMEDRIVE + HOMEPATH.
780                  */
781                 char *drive = getenv("HOMEDRIVE");
782                 char *path  = getenv("HOMEPATH");
783                 if (drive != NULL && path != NULL)
784                 {
785                         char *env = (char *) calloc(strlen(drive) + 
786                                         strlen(path) + 6, sizeof(char));
787                         strcpy(env, "HOME=");
788                         strcat(env, drive);
789                         strcat(env, path);
790                         putenv(env);
791                 }
792         }
793 #endif /* WIN32 */
794
795         /*
796          * Process command line arguments.
797          */
798         parse_args(argc, argv);
799         init_tables();
800
801         /*
802          * Open the input file.
803          */
804         if (strcmp(infile, "-") == 0)
805                 desc = stdin;
806         else if ((desc = fopen(infile, "r")) == NULL)
807         {
808 #if HAVE_PERROR
809                 perror(infile);
810 #else
811                 fprintf(stderr, "Cannot open %s\n", infile);
812 #endif
813                 usage();
814         }
815
816         /*
817          * Read and parse the input file, one line at a time.
818          */
819         errors = 0;
820         linenum = 0;
821         while (fgets(line, sizeof(line), desc) != NULL)
822         {
823                 ++linenum;
824                 parse_line(line);
825         }
826
827         /*
828          * Write the output file.
829          * If no output file was specified, use "$HOME/.less"
830          */
831         if (errors > 0)
832         {
833                 fprintf(stderr, "%d errors; no output produced\n", errors);
834                 exit(1);
835         }
836
837         if (outfile == NULL)
838                 outfile = getenv("LESSKEY");
839         if (outfile == NULL)
840                 outfile = homefile(LESSKEYFILE);
841         if ((out = fopen(outfile, "wb")) == NULL)
842         {
843 #if HAVE_PERROR
844                 perror(outfile);
845 #else
846                 fprintf(stderr, "Cannot open %s\n", outfile);
847 #endif
848                 exit(1);
849         }
850
851         /* File header */
852         fputbytes(out, fileheader, sizeof(fileheader));
853
854         /* Command key section */
855         fputbytes(out, cmdsection, sizeof(cmdsection));
856         fputint(out, cmdtable.pbuffer - cmdtable.buffer);
857         fputbytes(out, (char *)cmdtable.buffer, cmdtable.pbuffer-cmdtable.buffer);
858         /* Edit key section */
859         fputbytes(out, editsection, sizeof(editsection));
860         fputint(out, edittable.pbuffer - edittable.buffer);
861         fputbytes(out, (char *)edittable.buffer, edittable.pbuffer-edittable.buffer);
862
863         /* Environment variable section */
864         fputbytes(out, varsection, sizeof(varsection)); 
865         fputint(out, vartable.pbuffer - vartable.buffer);
866         fputbytes(out, (char *)vartable.buffer, vartable.pbuffer-vartable.buffer);
867
868         /* File trailer */
869         fputbytes(out, endsection, sizeof(endsection));
870         fputbytes(out, filetrailer, sizeof(filetrailer));
871         return (0);
872 }