75783969122abad1cf6a99215ab6d487e8edff06
[platform/upstream/nasm.git] / nasm.c
1 /* The Netwide Assembler main program module
2  *
3  * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
4  * Julian Hall. All rights reserved. The software is
5  * redistributable under the licence given in the file "Licence"
6  * distributed in the NASM archive.
7  */
8
9 #include <stdio.h>
10 #include <stdarg.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <ctype.h>
14
15 #include "nasm.h"
16 #include "nasmlib.h"
17 #include "preproc.h"
18 #include "parser.h"
19 #include "assemble.h"
20 #include "labels.h"
21 #include "outform.h"
22
23 static void report_error (int, char *, ...);
24 static void parse_cmdline (int, char **);
25 static void assemble_file (char *);
26 static int getkw (char *buf, char **value);
27 static void register_output_formats(void);
28 static void usage(void);
29
30 static char *obuf;
31 static char inname[FILENAME_MAX];
32 static char outname[FILENAME_MAX];
33 static char realout[FILENAME_MAX];
34 static int lineno;                     /* for error reporting */
35 static int lineinc;                    /* set by [LINE] or [ONELINE] */
36 static int globallineno;               /* for forward-reference tracking */
37 static int pass;
38 static struct ofmt *ofmt = NULL;
39
40 static FILE *ofile = NULL;
41 static int sb = 16;                    /* by default */
42
43 static long current_seg;
44 static struct RAA *offsets;
45 static long abs_offset;
46
47 static struct SAA *forwrefs;           /* keep track of forward references */
48 static int forwline;
49
50 static Preproc *preproc;
51 static int preprocess_only;
52
53 /* used by error function to report location */
54 static char currentfile[FILENAME_MAX];
55
56 /*
57  * This is a null preprocessor which just copies lines from input
58  * to output. It's used when someone explicitly requests that NASM
59  * not preprocess their source file.
60  */
61
62 static void no_pp_reset (char *, efunc);
63 static char *no_pp_getline (void);
64 static void no_pp_cleanup (void);
65 static Preproc no_pp = {
66     no_pp_reset,
67     no_pp_getline,
68     no_pp_cleanup
69 };
70
71 /*
72  * get/set current offset...
73  */
74 #define get_curr_ofs (current_seg==NO_SEG?abs_offset:\
75                       raa_read(offsets,current_seg))
76 #define set_curr_ofs(x) (current_seg==NO_SEG?(void)(abs_offset=(x)):\
77                          (void)(offsets=raa_write(offsets,current_seg,(x))))
78
79 static int want_usage;
80 static int terminate_after_phase;
81
82 int main(int argc, char **argv) {
83     want_usage = terminate_after_phase = FALSE;
84
85     nasm_set_malloc_error (report_error);
86     offsets = raa_init();
87     forwrefs = saa_init ((long)sizeof(int));
88
89     preproc = &nasmpp;
90     preprocess_only = FALSE;
91
92     seg_init();
93
94     register_output_formats();
95
96     parse_cmdline(argc, argv);
97
98     if (terminate_after_phase) {
99         if (want_usage)
100             usage();
101         return 1;
102     }
103
104     if (preprocess_only) {
105         char *line;
106
107         if (*outname) {
108             ofile = fopen(outname, "w");
109             if (!ofile)
110                 report_error (ERR_FATAL | ERR_NOFILE,
111                               "unable to open output file `%s'", outname);
112         } else
113             ofile = NULL;
114         preproc->reset (inname, report_error);
115         strcpy(currentfile,inname);
116         lineno = 0;
117         lineinc = 1;
118         while ( (line = preproc->getline()) ) {
119             lineno += lineinc;
120             if (ofile) {
121                 fputs(line, ofile);
122                 fputc('\n', ofile);
123             } else
124                 puts(line);
125             nasm_free (line);
126         }
127         preproc->cleanup();
128         if (ofile)
129             fclose(ofile);
130         if (ofile && terminate_after_phase)
131             remove(outname);
132     } else {
133         if (!*outname) {
134             ofmt->filename (inname, realout, report_error);
135             strcpy(outname, realout);
136         }
137
138         ofile = fopen(outname, "wb");
139         if (!ofile) {
140             report_error (ERR_FATAL | ERR_NOFILE,
141                           "unable to open output file `%s'", outname);
142         }
143         ofmt->init (ofile, report_error, define_label);
144         assemble_file (inname);
145         if (!terminate_after_phase) {
146             ofmt->cleanup ();
147             cleanup_labels ();
148         }
149         fclose (ofile);
150         if (terminate_after_phase)
151             remove(outname);
152     }
153
154     if (want_usage)
155         usage();
156     raa_free (offsets);
157     saa_free (forwrefs);
158
159     return 0;
160 }
161
162 static void parse_cmdline(int argc, char **argv) {
163     char *param;
164
165     *inname = *outname = '\0';
166     while (--argc) {
167         char *p = *++argv;
168         if (p[0]=='-') {
169             switch (p[1]) {
170               case 'o':                /* these parameters take values */
171               case 'f':
172                 if (p[2])              /* the parameter's in the option */
173                     param = p+2;
174                 else if (!argv[1]) {
175                     report_error (ERR_NONFATAL | ERR_NOFILE | ERR_USAGE,
176                                   "option `-%c' requires an argument",
177                                   p[1]);
178                     break;
179                 } else
180                     --argc, param = *++argv;
181                 if (p[1]=='o') {       /* output file */
182                     strcpy (outname, param);
183                 } else if (p[1]=='f') { /* output format */
184                     ofmt = ofmt_find(param);
185                     if (!ofmt) {
186                         report_error (ERR_FATAL | ERR_NOFILE | ERR_USAGE,
187                                       "unrecognised output format `%s'",
188                                       param);
189                     }
190                 }
191                 break;
192               case 'h':
193                 fprintf(stderr,
194                         "usage: nasm [-o outfile] [-f format]"
195                         " [-a] [-e] filename\n");
196                 fprintf(stderr,
197                         "    or nasm -r   for version info\n\n");
198                 fprintf(stderr,
199                         "    -e means preprocess only; "
200                         "-a means don't preprocess\n\n");
201                 fprintf(stderr,
202                         "valid output formats for -f are"
203                         " (`*' denotes default):\n");
204                 ofmt_list(ofmt);
205                 exit (0);              /* never need usage message here */
206                 break;
207               case 'r':
208                 fprintf(stderr, "NASM version %s\n", NASM_VER);
209                 exit (0);              /* never need usage message here */
210                 break;
211               case 'e':                /* preprocess only */
212                 preprocess_only = TRUE;
213                 break;
214               case 'a':                /* assemble only - don't preprocess */
215                 preproc = &no_pp;
216                 break;
217               default:
218                 report_error (ERR_NONFATAL | ERR_NOFILE | ERR_USAGE,
219                               "unrecognised option `-%c'",
220                               p[1]);
221                 break;
222             }
223         } else {
224             if (*inname) {
225                 report_error (ERR_NONFATAL | ERR_NOFILE | ERR_USAGE,
226                               "more than one input file specified");
227             } else
228                 strcpy(inname, p);
229         }
230     }
231     if (!*inname)
232         report_error (ERR_NONFATAL | ERR_NOFILE | ERR_USAGE,
233                       "no input file specified");
234 }
235
236 static void assemble_file (char *fname) {
237     char *value, *p, *line;
238     insn output_ins;
239     int i, rn_error;
240     long seg;
241
242     init_labels ();
243
244     /* pass one */
245     pass = 1;
246     current_seg = ofmt->section(NULL, pass, &sb);
247     preproc->reset(fname, report_error);
248     strcpy(currentfile,fname);
249     lineno = 0;
250     lineinc = 1;
251     globallineno = 0;
252     while ( (line = preproc->getline()) ) {
253         lineno += lineinc;
254         globallineno++;
255
256         if (line[0] == '%') {
257             int ln, li;
258             char buf[FILENAME_MAX];
259
260             /*
261              * This will be a line number directive. They come
262              * straight from the preprocessor, so we'll subject
263              * them to only minimal error checking.
264              */
265             if (strncmp(line, "%line", 5)) {
266                 if (preproc == &no_pp)
267                     report_error (ERR_WARNING, "unknown `%%' directive in "
268                                   " preprocessed source");
269             } else if (sscanf(line, "%%line %d+%d %s", &ln, &li, buf) != 3) {
270                 report_error (ERR_WARNING, "bogus line number directive in"
271                               " preprocessed source");
272             } else {
273                 lineno = ln - li;
274                 lineinc = li;
275                 strncpy (currentfile, buf, FILENAME_MAX-1);
276                 currentfile[FILENAME_MAX-1] = '\0';
277             }
278             continue;
279         }
280
281         /* here we parse our directives; this is not handled by the 'real'
282          * parser. */
283         if ( (i = getkw (line, &value)) ) {
284             switch (i) {
285               case 1:          /* [SEGMENT n] */
286                 seg = ofmt->section (value, pass, &sb);
287                 if (seg == NO_SEG) {
288                     report_error (ERR_NONFATAL,
289                                   "segment name `%s' not recognised",
290                                   value);
291                 } else {
292                     current_seg = seg;
293                 }
294                 break;
295               case 2:          /* [EXTERN label] */
296                 if (*value == '$')
297                     value++;           /* skip initial $ if present */
298                 declare_as_global (value, report_error);
299                 define_label (value, seg_alloc(), 0L, ofmt, report_error);
300                 break;
301               case 3:          /* [BITS bits] */
302                 switch (atoi(value)) {
303                   case 16:
304                   case 32:
305                     sb = atoi(value);
306                     break;
307                   default:
308                     report_error(ERR_NONFATAL,
309                                  "`%s' is not a valid argument to [BITS]",
310                                  value);
311                     break;
312                 }
313                 break;
314               case 4:          /* [GLOBAL symbol] */
315                 if (*value == '$')
316                     value++;           /* skip initial $ if present */
317                 declare_as_global (value, report_error);
318                 break;
319               case 5:          /* [COMMON symbol size] */
320                 p = value;
321                 while (*p && !isspace(*p))
322                     p++;
323                 if (*p) {
324                     long size;
325
326                     while (*p && isspace(*p))
327                         *p++ = '\0';
328                     size = readnum (p, &rn_error);
329                     if (rn_error)
330                         report_error (ERR_NONFATAL, "invalid size specified"
331                                       " in COMMON declaration");
332                     else
333                         define_common (value, seg_alloc(), size,
334                                        ofmt, report_error);
335                 } else
336                     report_error (ERR_NONFATAL, "no size specified in"
337                                   " COMMON declaration");
338                 break;
339               case 6:                  /* [ABSOLUTE address] */
340                 current_seg = NO_SEG;
341                 abs_offset = readnum(value, &rn_error);
342                 if (rn_error) {
343                     report_error (ERR_NONFATAL, "invalid address specified"
344                                   " for ABSOLUTE directive");
345                     abs_offset = 0x100;/* don't go near zero in case of / */
346                 }
347                 break;
348               default:
349                 if (!ofmt->directive (line+1, value, 1))
350                     report_error (ERR_NONFATAL, "unrecognised directive [%s]",
351                                   line+1);
352                 break;
353             }
354         } else {
355             long offs = get_curr_ofs;
356             parse_line (current_seg, offs, lookup_label,
357                         1, line, &output_ins, ofmt, report_error);
358             if (output_ins.forw_ref)
359                 *(int *)saa_wstruct(forwrefs) = globallineno;
360
361             /*
362              * Hack to prevent phase error in the code
363              *   rol ax,x
364              *   x equ 1
365              *
366              * We rule that the presence of a forward reference
367              * cancels out the UNITY property of the number 1. This
368              * isn't _strictly_ necessary in pass one, since the
369              * problem occurs in pass two, but for the sake of
370              * having the passes as near to identical as we can
371              * manage, we do it like this.
372              */
373             if (output_ins.forw_ref) {
374                 int i;
375                 for (i=0; i<output_ins.operands; i++)
376                     output_ins.oprs[i].type &= ~ONENESS;
377             }
378
379             if (output_ins.opcode == I_EQU) {
380                 /*
381                  * Special `..' EQUs get processed in pass two.
382                  */
383                 if (!output_ins.label)
384                     report_error (ERR_NONFATAL,
385                                   "EQU not preceded by label");
386                 else if (output_ins.label[0] != '.' ||
387                          output_ins.label[1] != '.') {
388                     if (output_ins.operands == 1 &&
389                         (output_ins.oprs[0].type & IMMEDIATE)) {
390                         define_label (output_ins.label,
391                                       output_ins.oprs[0].segment,
392                                       output_ins.oprs[0].offset,
393                                       ofmt, report_error);
394                     } else if (output_ins.operands == 2 &&
395                                (output_ins.oprs[0].type & IMMEDIATE) &&
396                                (output_ins.oprs[0].type & COLON) &&
397                                output_ins.oprs[0].segment == NO_SEG &&
398                                (output_ins.oprs[1].type & IMMEDIATE) &&
399                                output_ins.oprs[1].segment == NO_SEG) {
400                         define_label (output_ins.label,
401                                       output_ins.oprs[0].offset | SEG_ABS,
402                                       output_ins.oprs[1].offset,
403                                       ofmt, report_error);
404                     } else
405                         report_error(ERR_NONFATAL, "bad syntax for EQU");
406                 }
407             } else {
408                 if (output_ins.label)
409                     define_label (output_ins.label,
410                                   current_seg, offs,
411                                   ofmt, report_error);
412                 offs += insn_size (current_seg, offs, sb,
413                                    &output_ins, report_error);
414                 set_curr_ofs (offs);
415             }
416             cleanup_insn (&output_ins);
417         }
418         nasm_free (line);
419     }
420     preproc->cleanup();
421
422     if (terminate_after_phase) {
423         fclose(ofile);
424         remove(outname);
425         if (want_usage)
426             usage();
427         exit (1);
428     }
429
430     /* pass two */
431     pass = 2;
432     saa_rewind (forwrefs);
433     {
434         int *p = saa_rstruct (forwrefs);
435         if (p)
436             forwline = *p;
437         else
438             forwline = -1;
439     }
440     current_seg = ofmt->section(NULL, pass, &sb);
441     raa_free (offsets);
442     offsets = raa_init();
443     preproc->reset(fname, report_error);
444     strcpy(currentfile,fname);
445     lineno = 0;
446     lineinc = 1;
447     globallineno = 0;
448     while ( (line = preproc->getline()) ) {
449         lineno += lineinc;
450         globallineno++;
451
452         if (line[0] == '%') {
453             int ln, li;
454             char buf[FILENAME_MAX];
455
456             /*
457              * This will be a line number directive. They come
458              * straight from the preprocessor, so we'll subject
459              * them to only minimal error checking.
460              */
461             if (!strncmp(line, "%line", 5) &&
462                 sscanf(line, "%%line %d+%d %s", &ln, &li, buf) == 3) {
463                 lineno = ln - li;
464                 lineinc = li;
465                 strncpy (currentfile, buf, FILENAME_MAX-1);
466                 currentfile[FILENAME_MAX-1] = '\0';
467             }
468             continue;
469         }
470
471         /* here we parse our directives; this is not handled by
472          * the 'real' parser. */
473
474         if ( (i = getkw (line, &value)) ) {
475             switch (i) {
476               case 1:          /* [SEGMENT n] */
477                 seg = ofmt->section (value, pass, &sb);
478                 if (seg == NO_SEG) {
479                     report_error (ERR_PANIC,
480                                   "invalid segment name on pass two");
481                 } else
482                     current_seg = seg;
483                 break;
484               case 2:          /* [EXTERN label] */
485                 break;
486               case 3:          /* [BITS bits] */
487                 switch (atoi(value)) {
488                   case 16:
489                   case 32:
490                     sb = atoi(value);
491                     break;
492                   default:
493                     report_error(ERR_PANIC,
494                                  "invalid [BITS] value on pass two",
495                                  value);
496                     break;
497                 }
498                 break;
499               case 4:                  /* [GLOBAL symbol] */
500                 break;
501               case 5:                  /* [COMMON symbol size] */
502                 break;
503               case 6:                  /* [ABSOLUTE addr] */
504                 current_seg = NO_SEG;
505                 abs_offset = readnum(value, &rn_error);
506                 if (rn_error)
507                     report_error (ERR_PANIC, "invalid ABSOLUTE address "
508                                   "in pass two");
509                 break;
510               default:
511                 if (!ofmt->directive (line+1, value, 2))
512                     report_error (ERR_PANIC, "invalid directive on pass two");
513                 break;
514             }
515         } else {
516             long offs = get_curr_ofs;
517             parse_line (current_seg, offs, lookup_label, 2,
518                         line, &output_ins, ofmt, report_error);
519             if (globallineno == forwline) {
520                 int *p = saa_rstruct (forwrefs);
521                 if (p)
522                     forwline = *p;
523                 else
524                     forwline = -1;
525                 output_ins.forw_ref = TRUE;
526             } else
527                 output_ins.forw_ref = FALSE;
528
529             /*
530              * Hack to prevent phase error in the code
531              *   rol ax,x
532              *   x equ 1
533              */
534             if (output_ins.forw_ref) {
535                 int i;
536                 for (i=0; i<output_ins.operands; i++)
537                     output_ins.oprs[i].type &= ~ONENESS;
538             }
539
540             obuf = line;
541             if (output_ins.label)
542                 define_label_stub (output_ins.label, report_error);
543             if (output_ins.opcode == I_EQU) {
544                 /*
545                  * Special `..' EQUs get processed here.
546                  */
547                 if (output_ins.label[0] == '.' &&
548                     output_ins.label[1] == '.') {
549                     if (output_ins.operands == 1 &&
550                         (output_ins.oprs[0].type & IMMEDIATE)) {
551                         define_label (output_ins.label,
552                                       output_ins.oprs[0].segment,
553                                       output_ins.oprs[0].offset,
554                                       ofmt, report_error);
555                     } else if (output_ins.operands == 2 &&
556                                (output_ins.oprs[0].type & IMMEDIATE) &&
557                                (output_ins.oprs[0].type & COLON) &&
558                                output_ins.oprs[0].segment == NO_SEG &&
559                                (output_ins.oprs[1].type & IMMEDIATE) &&
560                                output_ins.oprs[1].segment == NO_SEG) {
561                         define_label (output_ins.label,
562                                       output_ins.oprs[0].offset | SEG_ABS,
563                                       output_ins.oprs[1].offset,
564                                       ofmt, report_error);
565                     } else
566                         report_error(ERR_NONFATAL, "bad syntax for EQU");
567                 }
568             }
569             offs += assemble (current_seg, offs, sb,
570                               &output_ins, ofmt, report_error);
571             cleanup_insn (&output_ins);
572             set_curr_ofs (offs);
573         }
574         nasm_free (line);
575     }
576     preproc->cleanup();
577 }
578
579 static int getkw (char *buf, char **value) {
580     char *p, *q;
581
582     if (*buf!='[')
583         return 0;
584     p = buf;
585     while (*p && *p != ']') p++;
586     if (!*p)
587         return 0;
588     q = p++;
589     while (*p && *p != ';') {
590         if (!isspace(*p))
591             return 0;
592         p++;
593     }
594     q[1] = '\0';
595
596     p = buf+1;
597     while (*buf && *buf!=' ' && *buf!=']' && *buf!='\t')
598         buf++;
599     if (*buf==']') {
600         *buf = '\0';
601         *value = buf;
602     } else {
603         *buf++ = '\0';
604         *value = buf;
605         while (*buf!=']') buf++;
606         *buf++ = '\0';
607     }
608     for (q=p; *q; q++)
609         *q = tolower(*q);
610     if (!strcmp(p, "segment") || !strcmp(p, "section"))
611         return 1;
612     if (!strcmp(p, "extern"))
613         return 2;
614     if (!strcmp(p, "bits"))
615         return 3;
616     if (!strcmp(p, "global"))
617         return 4;
618     if (!strcmp(p, "common"))
619         return 5;
620     if (!strcmp(p, "absolute"))
621         return 6;
622     return -1;
623 }
624
625 static void report_error (int severity, char *fmt, ...) {
626     va_list ap;
627
628     if (severity & ERR_NOFILE)
629         fputs ("nasm: ", stderr);
630     else
631         fprintf (stderr, "%s:%d: ", currentfile,
632                  lineno + (severity & ERR_OFFBY1 ? lineinc : 0));
633
634     if ( (severity & ERR_MASK) == ERR_WARNING)
635         fputs ("warning: ", stderr);
636     else if ( (severity & ERR_MASK) == ERR_PANIC)
637         fputs ("panic: ", stderr);
638
639     va_start (ap, fmt);
640     vfprintf (stderr, fmt, ap);
641     fputc ('\n', stderr);
642
643     if (severity & ERR_USAGE)
644         want_usage = TRUE;
645
646     switch (severity & ERR_MASK) {
647       case ERR_WARNING:
648         /* no further action, by definition */
649         break;
650       case ERR_NONFATAL:
651         terminate_after_phase = TRUE;
652         break;
653       case ERR_FATAL:
654         if (ofile) {
655             fclose(ofile);
656             remove(outname);
657         }
658         if (want_usage)
659             usage();
660         exit(1);                       /* instantly die */
661         break;                         /* placate silly compilers */
662       case ERR_PANIC:
663         abort();                       /* halt, catch fire, and dump core */
664         break;
665     }
666 }
667
668 static void usage(void) {
669     fputs("type `nasm -h' for help\n", stderr);
670 }
671
672 static void register_output_formats(void) {
673     /* Flat-form binary format */
674 #ifdef OF_BIN
675     extern struct ofmt of_bin;
676 #endif
677     /* Unix formats: a.out, COFF, ELF */
678 #ifdef OF_AOUT
679     extern struct ofmt of_aout;
680 #endif
681 #ifdef OF_COFF
682     extern struct ofmt of_coff;
683 #endif
684 #ifdef OF_ELF
685     extern struct ofmt of_elf;
686 #endif
687     /* Linux strange format: as86 */
688 #ifdef OF_AS86
689     extern struct ofmt of_as86;
690 #endif
691     /* DOS formats: OBJ, Win32 */
692 #ifdef OF_OBJ
693     extern struct ofmt of_obj;
694 #endif
695 #ifdef OF_WIN32
696     extern struct ofmt of_win32;
697 #endif
698 #ifdef OF_RDF
699     extern struct ofmt of_rdf;
700 #endif
701 #ifdef OF_DBG     /* debug format must be included specifically */
702     extern struct ofmt of_dbg;
703 #endif
704
705 #ifdef OF_BIN
706     ofmt_register (&of_bin);
707 #endif
708 #ifdef OF_AOUT
709     ofmt_register (&of_aout);
710 #endif
711 #ifdef OF_COFF
712     ofmt_register (&of_coff);
713 #endif
714 #ifdef OF_ELF
715     ofmt_register (&of_elf);
716 #endif
717 #ifdef OF_AS86
718     ofmt_register (&of_as86);
719 #endif
720 #ifdef OF_OBJ
721     ofmt_register (&of_obj);
722 #endif
723 #ifdef OF_WIN32
724     ofmt_register (&of_win32);
725 #endif
726 #ifdef OF_RDF
727     ofmt_register (&of_rdf);
728 #endif
729 #ifdef OF_DBG
730     ofmt_register (&of_dbg);
731 #endif
732     /*
733      * set the default format
734      */
735     ofmt = &OF_DEFAULT;
736 }
737
738 #define BUF_DELTA 512
739
740 static FILE *no_pp_fp;
741 static efunc no_pp_err;
742
743 static void no_pp_reset (char *file, efunc error) {
744     no_pp_err = error;
745     no_pp_fp = fopen(file, "r");
746     if (!no_pp_fp)
747         no_pp_err (ERR_FATAL | ERR_NOFILE,
748                    "unable to open input file `%s'", file);
749 }
750
751 static char *no_pp_getline (void) {
752     char *buffer, *p, *q;
753     int bufsize;
754
755     bufsize = BUF_DELTA;
756     buffer = nasm_malloc(BUF_DELTA);
757     p = buffer;
758     while (1) {
759         q = fgets(p, bufsize-(p-buffer), no_pp_fp);
760         if (!q)
761             break;
762         p += strlen(p);
763         if (p > buffer && p[-1] == '\n')
764             break;
765         if (p-buffer > bufsize-10) {
766             bufsize += BUF_DELTA;
767             buffer = nasm_realloc(buffer, bufsize);
768         }
769     }
770
771     if (!q && p == buffer) {
772         nasm_free (buffer);
773         return NULL;
774     }
775
776     /*
777      * Play safe: remove CRs as well as LFs, if any of either are
778      * present at the end of the line.
779      */
780     while (p > buffer && (p[-1] == '\n' || p[-1] == '\r'))
781         *--p = '\0';
782
783     /*
784      * Handle spurious ^Z, which may be inserted into source files
785      * by some file transfer utilities.
786      */
787     buffer[strcspn(buffer, "\032")] = '\0';
788
789     return buffer;
790 }
791
792 static void no_pp_cleanup (void) {
793     fclose(no_pp_fp);
794 }