]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/less/lesskey.c
This commit was generated by cvs2svn to compensate for changes in r117535,
[FreeBSD/FreeBSD.git] / contrib / less / lesskey.c
1 /*
2  * Copyright (C) 1984-2000  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)
356         char **pp;
357 {
358         register char *p;
359         register char ch;
360         register int i;
361         static char buf[10];
362         static char tstr_control_k[] =
363                 { SK_SPECIAL_KEY, SK_CONTROL_K, 6, 1, 1, 1, '\0' };
364
365         p = *pp;
366         switch (*p)
367         {
368         case '\\':
369                 ++p;
370                 switch (*p)
371                 {
372                 case '0': case '1': case '2': case '3':
373                 case '4': case '5': case '6': case '7':
374                         /*
375                          * Parse an octal number.
376                          */
377                         ch = 0;
378                         i = 0;
379                         do
380                                 ch = 8*ch + (*p - '0');
381                         while (*++p >= '0' && *p <= '7' && ++i < 3);
382                         *pp = p;
383                         if (ch == CONTROL('K'))
384                                 return tstr_control_k;
385                         buf[0] = ch;
386                         buf[1] = '\0';
387                         return (buf);
388                 case 'b':
389                         *pp = p+1;
390                         return ("\b");
391                 case 'e':
392                         *pp = p+1;
393                         buf[0] = ESC;
394                         buf[1] = '\0';
395                         return (buf);
396                 case 'n':
397                         *pp = p+1;
398                         return ("\n");
399                 case 'r':
400                         *pp = p+1;
401                         return ("\r");
402                 case 't':
403                         *pp = p+1;
404                         return ("\t");
405                 case 'k':
406                         switch (*++p)
407                         {
408                         case 'u': ch = SK_UP_ARROW; break;
409                         case 'd': ch = SK_DOWN_ARROW; break;
410                         case 'r': ch = SK_RIGHT_ARROW; break;
411                         case 'l': ch = SK_LEFT_ARROW; break;
412                         case 'U': ch = SK_PAGE_UP; break;
413                         case 'D': ch = SK_PAGE_DOWN; break;
414                         case 'h': ch = SK_HOME; break;
415                         case 'e': ch = SK_END; break;
416                         case 'x': ch = SK_DELETE; break;
417                         default:
418                                 error("illegal char after \\k");
419                                 *pp = p+1;
420                                 return ("");
421                         }
422                         *pp = p+1;
423                         buf[0] = SK_SPECIAL_KEY;
424                         buf[1] = ch;
425                         buf[2] = 6;
426                         buf[3] = 1;
427                         buf[4] = 1;
428                         buf[5] = 1;
429                         buf[6] = '\0';
430                         return (buf);
431                 default:
432                         /*
433                          * Backslash followed by any other char 
434                          * just means that char.
435                          */
436                         *pp = p+1;
437                         buf[0] = *p;
438                         buf[1] = '\0';
439                         if (buf[0] == CONTROL('K'))
440                                 return tstr_control_k;
441                         return (buf);
442                 }
443         case '^':
444                 /*
445                  * Carat means CONTROL.
446                  */
447                 *pp = p+2;
448                 buf[0] = CONTROL(p[1]);
449                 buf[1] = '\0';
450                 if (buf[0] == CONTROL('K'))
451                         return tstr_control_k;
452                 return (buf);
453         }
454         *pp = p+1;
455         buf[0] = *p;
456         buf[1] = '\0';
457         if (buf[0] == CONTROL('K'))
458                 return tstr_control_k;
459         return (buf);
460 }
461
462 /*
463  * Skip leading spaces in a string.
464  */
465         public char *
466 skipsp(s)
467         register char *s;
468 {
469         while (*s == ' ' || *s == '\t') 
470                 s++;
471         return (s);
472 }
473
474 /*
475  * Skip non-space characters in a string.
476  */
477         public char *
478 skipnsp(s)
479         register char *s;
480 {
481         while (*s != '\0' && *s != ' ' && *s != '\t')
482                 s++;
483         return (s);
484 }
485
486 /*
487  * Clean up an input line:
488  * strip off the trailing newline & any trailing # comment.
489  */
490         char *
491 clean_line(s)
492         char *s;
493 {
494         register int i;
495
496         s = skipsp(s);
497         for (i = 0;  s[i] != '\n' && s[i] != '\r' && s[i] != '\0';  i++)
498                 if (s[i] == '#' && (i == 0 || s[i-1] != '\\'))
499                         break;
500         s[i] = '\0';
501         return (s);
502 }
503
504 /*
505  * Add a byte to the output command table.
506  */
507         void
508 add_cmd_char(c)
509         int c;
510 {
511         if (currtable->pbuffer >= currtable->buffer + MAX_USERCMD)
512         {
513                 error("too many commands");
514                 exit(1);
515         }
516         *(currtable->pbuffer)++ = c;
517 }
518
519 /*
520  * Add a string to the output command table.
521  */
522         void
523 add_cmd_str(s)
524         char *s;
525 {
526         for ( ;  *s != '\0';  s++)
527                 add_cmd_char(*s);
528 }
529
530 /*
531  * See if we have a special "control" line.
532  */
533         int
534 control_line(s)
535         char *s;
536 {
537 #define PREFIX(str,pat) (strncmp(str,pat,strlen(pat)-1) == 0)
538
539         if (PREFIX(s, "#line-edit"))
540         {
541                 currtable = &edittable;
542                 return (1);
543         }
544         if (PREFIX(s, "#command"))
545         {
546                 currtable = &cmdtable;
547                 return (1);
548         }
549         if (PREFIX(s, "#env"))
550         {
551                 currtable = &vartable;
552                 return (1);
553         }
554         if (PREFIX(s, "#stop"))
555         {
556                 add_cmd_char('\0');
557                 add_cmd_char(A_END_LIST);
558                 return (1);
559         }
560         return (0);
561 }
562
563 /*
564  * Output some bytes.
565  */
566         void
567 fputbytes(fd, buf, len)
568         FILE *fd;
569         char *buf;
570         int len;
571 {
572         while (len-- > 0)
573         {
574                 fwrite(buf, sizeof(char), 1, fd);
575                 buf++;
576         }
577 }
578
579 /*
580  * Output an integer, in special KRADIX form.
581  */
582         void
583 fputint(fd, val)
584         FILE *fd;
585         unsigned int val;
586 {
587         char c;
588
589         if (val >= KRADIX*KRADIX)
590         {
591                 fprintf(stderr, "error: integer too big (%d > %d)\n", 
592                         val, KRADIX*KRADIX);
593                 exit(1);
594         }
595         c = val % KRADIX;
596         fwrite(&c, sizeof(char), 1, fd);
597         c = val / KRADIX;
598         fwrite(&c, sizeof(char), 1, fd);
599 }
600
601 /*
602  * Find an action, given the name of the action.
603  */
604         int
605 findaction(actname)
606         char *actname;
607 {
608         int i;
609
610         for (i = 0;  currtable->names[i].cn_name != NULL;  i++)
611                 if (strcmp(currtable->names[i].cn_name, actname) == 0)
612                         return (currtable->names[i].cn_action);
613         error("unknown action");
614         return (A_INVALID);
615 }
616
617         void
618 error(s)
619         char *s;
620 {
621         fprintf(stderr, "line %d: %s\n", linenum, s);
622         errors++;
623 }
624
625
626         void
627 parse_cmdline(p)
628         char *p;
629 {
630         int cmdlen;
631         char *actname;
632         int action;
633         char *s;
634         char c;
635
636         /*
637          * Parse the command string and store it in the current table.
638          */
639         cmdlen = 0;
640         do
641         {
642                 s = tstr(&p);
643                 cmdlen += strlen(s);
644                 if (cmdlen > MAX_CMDLEN)
645                         error("command too long");
646                 else
647                         add_cmd_str(s);
648         } while (*p != ' ' && *p != '\t' && *p != '\0');
649         /*
650          * Terminate the command string with a null byte.
651          */
652         add_cmd_char('\0');
653
654         /*
655          * Skip white space between the command string
656          * and the action name.
657          * Terminate the action name with a null byte.
658          */
659         p = skipsp(p);
660         if (*p == '\0')
661         {
662                 error("missing action");
663                 return;
664         }
665         actname = p;
666         p = skipnsp(p);
667         c = *p;
668         *p = '\0';
669
670         /*
671          * Parse the action name and store it in the current table.
672          */
673         action = findaction(actname);
674
675         /*
676          * See if an extra string follows the action name.
677          */
678         *p = c;
679         p = skipsp(p);
680         if (*p == '\0')
681         {
682                 add_cmd_char(action);
683         } else
684         {
685                 /*
686                  * OR the special value A_EXTRA into the action byte.
687                  * Put the extra string after the action byte.
688                  */
689                 add_cmd_char(action | A_EXTRA);
690                 while (*p != '\0')
691                         add_cmd_str(tstr(&p));
692                 add_cmd_char('\0');
693         }
694 }
695
696         void
697 parse_varline(p)
698         char *p;
699 {
700         char *s;
701
702         do
703         {
704                 s = tstr(&p);
705                 add_cmd_str(s);
706         } while (*p != ' ' && *p != '\t' && *p != '=' && *p != '\0');
707         /*
708          * Terminate the variable name with a null byte.
709          */
710         add_cmd_char('\0');
711
712         p = skipsp(p);
713         if (*p++ != '=')
714         {
715                 error("missing =");
716                 return;
717         }
718
719         add_cmd_char(EV_OK|A_EXTRA);
720
721         p = skipsp(p);
722         while (*p != '\0')
723         {
724                 s = tstr(&p);
725                 add_cmd_str(s);
726         }
727         add_cmd_char('\0');
728 }
729
730 /*
731  * Parse a line from the lesskey file.
732  */
733         void
734 parse_line(line)
735         char *line;
736 {
737         char *p;
738
739         /*
740          * See if it is a control line.
741          */
742         if (control_line(line))
743                 return;
744         /*
745          * Skip leading white space.
746          * Replace the final newline with a null byte.
747          * Ignore blank lines and comments.
748          */
749         p = clean_line(line);
750         if (*p == '\0')
751                 return;
752
753         if (currtable == &vartable)
754                 parse_varline(p);
755         else
756                 parse_cmdline(p);
757 }
758
759         int
760 main(argc, argv)
761         int argc;
762         char *argv[];
763 {
764         FILE *desc;
765         FILE *out;
766         char line[200];
767
768 #ifdef WIN32
769         if (getenv("HOME") == NULL)
770         {
771                 /*
772                  * If there is no HOME environment variable,
773                  * try the concatenation of HOMEDRIVE + HOMEPATH.
774                  */
775                 char *drive = getenv("HOMEDRIVE");
776                 char *path  = getenv("HOMEPATH");
777                 if (drive != NULL && path != NULL)
778                 {
779                         char *env = (char *) calloc(strlen(drive) + 
780                                         strlen(path) + 6, sizeof(char));
781                         strcpy(env, "HOME=");
782                         strcat(env, drive);
783                         strcat(env, path);
784                         putenv(env);
785                 }
786         }
787 #endif /* WIN32 */
788
789         /*
790          * Process command line arguments.
791          */
792         parse_args(argc, argv);
793         init_tables();
794
795         /*
796          * Open the input file.
797          */
798         if (strcmp(infile, "-") == 0)
799                 desc = stdin;
800         else if ((desc = fopen(infile, "r")) == NULL)
801         {
802 #if HAVE_PERROR
803                 perror(infile);
804 #else
805                 fprintf(stderr, "Cannot open %s\n", infile);
806 #endif
807                 usage();
808         }
809
810         /*
811          * Read and parse the input file, one line at a time.
812          */
813         errors = 0;
814         linenum = 0;
815         while (fgets(line, sizeof(line), desc) != NULL)
816         {
817                 ++linenum;
818                 parse_line(line);
819         }
820
821         /*
822          * Write the output file.
823          * If no output file was specified, use "$HOME/.less"
824          */
825         if (errors > 0)
826         {
827                 fprintf(stderr, "%d errors; no output produced\n", errors);
828                 exit(1);
829         }
830
831         if (outfile == NULL)
832                 outfile = getenv("LESSKEY");
833         if (outfile == NULL)
834                 outfile = homefile(LESSKEYFILE);
835         if ((out = fopen(outfile, "wb")) == NULL)
836         {
837 #if HAVE_PERROR
838                 perror(outfile);
839 #else
840                 fprintf(stderr, "Cannot open %s\n", outfile);
841 #endif
842                 exit(1);
843         }
844
845         /* File header */
846         fputbytes(out, fileheader, sizeof(fileheader));
847
848         /* Command key section */
849         fputbytes(out, cmdsection, sizeof(cmdsection));
850         fputint(out, cmdtable.pbuffer - cmdtable.buffer);
851         fputbytes(out, (char *)cmdtable.buffer, cmdtable.pbuffer-cmdtable.buffer);
852         /* Edit key section */
853         fputbytes(out, editsection, sizeof(editsection));
854         fputint(out, edittable.pbuffer - edittable.buffer);
855         fputbytes(out, (char *)edittable.buffer, edittable.pbuffer-edittable.buffer);
856
857         /* Environment variable section */
858         fputbytes(out, varsection, sizeof(varsection)); 
859         fputint(out, vartable.pbuffer - vartable.buffer);
860         fputbytes(out, (char *)vartable.buffer, vartable.pbuffer-vartable.buffer);
861
862         /* File trailer */
863         fputbytes(out, endsection, sizeof(endsection));
864         fputbytes(out, filetrailer, sizeof(filetrailer));
865         return (0);
866 }