]> git.xonotic.org Git - xonotic/gmqcc.git/blob - main.c
-O now, additionally to taking a number, can work like -W and -f to take an actual...
[xonotic/gmqcc.git] / main.c
1 /*
2  * Copyright (C) 2012
3  *     Dale Weiler
4  *     Wolfgang Bumiller
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy of
7  * this software and associated documentation files (the "Software"), to deal in
8  * the Software without restriction, including without limitation the rights to
9  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10  * of the Software, and to permit persons to whom the Software is furnished to do
11  * so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 #include "gmqcc.h"
25 #include "lexer.h"
26
27 uint32_t    opts_flags[1 + (COUNT_FLAGS / 32)];
28 uint32_t    opts_optimization[1 + (COUNT_OPTIMIZATIONS / 32)];
29
30 uint32_t    opts_O        = 1;
31 const char *opts_output   = "progs.dat";
32 int         opts_standard = COMPILER_GMQCC;
33 bool        opts_debug    = false;
34 bool        opts_memchk   = false;
35 bool        opts_dumpfin  = false;
36 bool        opts_dump     = false;
37 bool        opts_forcecrc = false;
38 bool        opts_pp_only  = false;
39 size_t      opts_max_array_size = 1024;
40
41 uint16_t    opts_forced_crc;
42
43 static bool opts_output_wasset = false;
44
45 /* set by the standard */
46 const oper_info *operators      = NULL;
47 size_t           operator_count = 0;
48
49 typedef struct { char *filename; int type; } argitem;
50 static argitem *items = NULL;
51
52 #define TYPE_QC  0
53 #define TYPE_ASM 1
54 #define TYPE_SRC 2
55
56 static const char *app_name;
57
58 static int usage() {
59     con_out("usage: %s [options] [files...]", app_name);
60     con_out("options:\n"
61             "  -h, --help             show this help message\n"
62             "  -debug                 turns on compiler debug messages\n"
63             "  -memchk                turns on compiler memory leak check\n");
64     con_out("  -o, --output=file      output file, defaults to progs.dat\n"
65             "  -a filename            add an asm file to be assembled\n"
66             "  -s filename            add a progs.src file to be used\n");
67     con_out("  -E                     stop after preprocessing\n");
68     con_out("  -f<flag>               enable a flag\n"
69             "  -fno-<flag>            disable a flag\n"
70             "  -std standard          select one of the following standards\n"
71             "       -std=qcc          original QuakeC\n"
72             "       -std=fteqcc       fteqcc QuakeC\n"
73             "       -std=gmqcc        this compiler (default)\n");
74     con_out("  -W<warning>            enable a warning\n"
75             "  -Wno-<warning>         disable a warning\n"
76             "  -Wall                  enable all warnings\n"
77             "  -Werror                treat warnings as errors\n");
78     con_out("  -force-crc=num         force a specific checksum into the header\n");
79     con_out("\n");
80     con_out("flags:\n"
81             "  -fadjust-vector-fields\n"
82             "            when assigning a vector field, its _y and _z fields also get assigned\n"
83            );
84     return -1;
85 }
86
87 static bool options_setflag_all(const char *name, bool on, uint32_t *flags, const opts_flag_def *list, size_t listsize) {
88     size_t i;
89
90     for (i = 0; i < listsize; ++i) {
91         if (!strcmp(name, list[i].name)) {
92             longbit lb = list[i].bit;
93 #if 0
94             if (on)
95                 flags[lb.idx] |= (1<<(lb.bit));
96             else
97                 flags[lb.idx] &= ~(1<<(lb.bit));
98 #else
99             if (on)
100                 flags[0] |= (1<<lb);
101             else
102                 flags[0] &= ~(1<<(lb));
103 #endif
104             return true;
105         }
106     }
107     return false;
108 }
109 static bool options_setflag(const char *name, bool on) {
110     return options_setflag_all(name, on, opts_flags, opts_flag_list, COUNT_FLAGS);
111 }
112 static bool options_setwarn(const char *name, bool on) {
113     return options_setflag_all(name, on, opts_warn, opts_warn_list, COUNT_WARNINGS);
114 }
115 static bool options_setoptim(const char *name, bool on) {
116     return options_setflag_all(name, on, opts_optimization, opts_opt_list, COUNT_OPTIMIZATIONS);
117 }
118
119 static bool options_witharg(int *argc_, char ***argv_, char **out) {
120     int  argc   = *argc_;
121     char **argv = *argv_;
122
123     if (argv[0][2]) {
124         *out = argv[0]+2;
125         return true;
126     }
127     /* eat up the next */
128     if (argc < 2) /* no parameter was provided */
129         return false;
130
131     *out = argv[1];
132     --*argc_;
133     ++*argv_;
134     return true;
135 }
136
137 static bool options_long_witharg_all(const char *optname, int *argc_, char ***argv_, char **out, int ds, bool split) {
138     int  argc   = *argc_;
139     char **argv = *argv_;
140
141     size_t len = strlen(optname);
142
143     if (strncmp(argv[0]+ds, optname, len))
144         return false;
145
146     /* it's --optname, check how the parameter is supplied */
147     if (argv[0][ds+len] == '=') {
148         /* using --opt=param */
149         *out = argv[0]+ds+len+1;
150         return true;
151     }
152
153     if (!split || argc < ds) /* no parameter was provided, or only single-arg form accepted */
154         return false;
155
156     /* using --opt param */
157     *out = argv[1];
158     --*argc_;
159     ++*argv_;
160     return true;
161 }
162 static bool options_long_witharg(const char *optname, int *argc_, char ***argv_, char **out) {
163     return options_long_witharg_all(optname, argc_, argv_, out, 2, true);
164 }
165 static bool options_long_gcc(const char *optname, int *argc_, char ***argv_, char **out) {
166     return options_long_witharg_all(optname, argc_, argv_, out, 1, false);
167 }
168
169 static void options_set(uint32_t *flags, size_t idx, bool on)
170 {
171     longbit lb = LONGBIT(idx);
172 #if 0
173     if (on)
174         flags[lb.idx] |= (1<<(lb.bit));
175     else
176         flags[lb.idx] &= ~(1<<(lb.bit));
177 #else
178     if (on)
179         flags[0] |= (1<<(lb));
180     else
181         flags[0] &= ~(1<<(lb));
182 #endif
183 }
184
185 static void set_optimizations(unsigned int level)
186 {
187     size_t i;
188     for (i = 0; i < COUNT_OPTIMIZATIONS; ++i)
189         options_set(opts_optimization, i, level >= opts_opt_oflag[i]);
190 }
191
192 static bool options_parse(int argc, char **argv) {
193     bool argend = false;
194     size_t itr;
195     char  buffer[1024];
196     char *redirout = (char*)stdout;
197     char *redirerr = (char*)stderr;
198
199     while (!argend && argc > 1) {
200         char *argarg;
201         argitem item;
202
203         ++argv;
204         --argc;
205
206         if (argv[0][0] == '-') {
207     /* All gcc-type long options */
208             if (options_long_gcc("std", &argc, &argv, &argarg)) {
209                 if      (!strcmp(argarg, "gmqcc") || !strcmp(argarg, "default")) {
210                     options_set(opts_flags, ADJUST_VECTOR_FIELDS, true);
211                     opts_standard = COMPILER_GMQCC;
212                 } else if (!strcmp(argarg, "qcc")) {
213                     options_set(opts_flags, ADJUST_VECTOR_FIELDS, false);
214                     options_set(opts_flags, ASSIGN_FUNCTION_TYPES, true);
215                     opts_standard = COMPILER_QCC;
216                 } else if (!strcmp(argarg, "fte") || !strcmp(argarg, "fteqcc")) {
217                     options_set(opts_flags, FTEPP,                true);
218                     options_set(opts_flags, TRANSLATABLE_STRINGS, true);
219                     options_set(opts_flags, ADJUST_VECTOR_FIELDS, false);
220                     options_set(opts_flags, ASSIGN_FUNCTION_TYPES, true);
221                     opts_standard = COMPILER_FTEQCC;
222                 } else if (!strcmp(argarg, "qccx")) {
223                     options_set(opts_flags, ADJUST_VECTOR_FIELDS, false);
224                     opts_standard = COMPILER_QCCX;
225                 } else {
226                     con_out("Unknown standard: %s\n", argarg);
227                     return false;
228                 }
229                 continue;
230             }
231             if (options_long_gcc("force-crc", &argc, &argv, &argarg)) {
232                 opts_forcecrc = true;
233                 opts_forced_crc = strtol(argarg, NULL, 0);
234                 continue;
235             }
236             if (options_long_gcc("redirout", &argc, &argv, &redirout)) {
237                 con_change(redirout, redirerr);
238                 continue;
239             }
240             if (options_long_gcc("redirerr", &argc, &argv, &redirerr)) {
241                 con_change(redirout, redirerr);
242                 continue;
243             }
244
245             /* show defaults (like pathscale) */
246             if (!strcmp(argv[0]+1, "show-defaults")) {
247                 size_t itr;
248                 char   buffer[1024];
249                 for (itr = 0; itr < COUNT_FLAGS; ++itr) {
250                     if (!OPTS_FLAG(itr))
251                         continue;
252
253                     memset(buffer, 0, sizeof(buffer));
254                     util_strtononcmd(opts_flag_list[itr].name, buffer, strlen(opts_flag_list[itr].name) + 1);
255
256                     con_out("-f%s ", buffer);
257                 }
258                 for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
259                     if (!OPTS_WARN(itr))
260                         continue;
261
262                     memset(buffer, 0, sizeof(buffer));
263                     util_strtononcmd(opts_warn_list[itr].name, buffer, strlen(opts_warn_list[itr].name) + 1);
264                     con_out("-W%s ", buffer);
265                 }
266                 con_out("\n");
267                 exit(0);
268             }
269
270             if (!strcmp(argv[0]+1, "debug")) {
271                 opts_debug = true;
272                 continue;
273             }
274             if (!strcmp(argv[0]+1, "dump")) {
275                 opts_dump = true;
276                 continue;
277             }
278             if (!strcmp(argv[0]+1, "dumpfin")) {
279                 opts_dumpfin = true;
280                 continue;
281             }
282             if (!strcmp(argv[0]+1, "memchk")) {
283                 opts_memchk = true;
284                 continue;
285             }
286             if (!strcmp(argv[0]+1, "nocolor")) {
287                 con_color(0);
288                 continue;
289             }
290
291             switch (argv[0][1]) {
292                 /* -h, show usage but exit with 0 */
293                 case 'h':
294                     usage();
295                     exit(0);
296                     /* break; never reached because of exit(0) */
297
298                 case 'E':
299                     opts_pp_only = true;
300                     break;
301
302                 /* handle all -fflags */
303                 case 'f':
304                     util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
305                     if (!strcmp(argv[0]+2, "HELP") || *(argv[0]+2) == '?') {
306                         con_out("Possible flags:\n");
307                         for (itr = 0; itr < COUNT_FLAGS; ++itr) {
308                             util_strtononcmd(opts_flag_list[itr].name, buffer, sizeof(buffer));
309                             con_out(" -f%s\n", buffer);
310                         }
311                         exit(0);
312                     }
313                     else if (!strncmp(argv[0]+2, "NO_", 3)) {
314                         if (!options_setflag(argv[0]+5, false)) {
315                             con_out("unknown flag: %s\n", argv[0]+2);
316                             return false;
317                         }
318                     }
319                     else if (!options_setflag(argv[0]+2, true)) {
320                         con_out("unknown flag: %s\n", argv[0]+2);
321                         return false;
322                     }
323                     break;
324                 case 'W':
325                     util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
326                     if (!strcmp(argv[0]+2, "HELP") || *(argv[0]+2) == '?') {
327                         con_out("Possible warnings:\n");
328                         for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
329                             util_strtononcmd(opts_warn_list[itr].name, buffer, sizeof(buffer));
330                             con_out(" -W%s\n", buffer);
331                         }
332                         exit(0);
333                     }
334                     else if (!strcmp(argv[0]+2, "NO_ERROR")) {
335                         opts_werror = false;
336                         break;
337                     }
338                     else if (!strcmp(argv[0]+2, "ERROR")) {
339                         opts_werror = true;
340                         break;
341                     }
342                     else if (!strcmp(argv[0]+2, "NONE")) {
343                         for (itr = 0; itr < sizeof(opts_warn)/sizeof(opts_warn[0]); ++itr)
344                             opts_warn[itr] = 0;
345                         break;
346                     }
347                     else if (!strcmp(argv[0]+2, "ALL")) {
348                         for (itr = 0; itr < sizeof(opts_warn)/sizeof(opts_warn[0]); ++itr)
349                             opts_warn[itr] = 0xFFFFFFFFL;
350                         break;
351                     }
352                     if (!strncmp(argv[0]+2, "NO_", 3)) {
353                         if (!options_setwarn(argv[0]+5, false)) {
354                             con_out("unknown warning: %s\n", argv[0]+2);
355                             return false;
356                         }
357                     }
358                     else if (!options_setwarn(argv[0]+2, true)) {
359                         con_out("unknown warning: %s\n", argv[0]+2);
360                         return false;
361                     }
362                     break;
363
364                 case 'O':
365                     if (!options_witharg(&argc, &argv, &argarg)) {
366                         con_out("option -O requires a numerical argument, or optimization name with an optional 'no-' prefix\n");
367                         return false;
368                     }
369                     if (isdigit(argarg[0])) {
370                         opts_O = atoi(argarg);
371                         set_optimizations(opts_O);
372                     } else {
373                         util_strtocmd(argarg, argarg, strlen(argarg)+1);
374                         if (!strcmp(argarg, "HELP")) {
375                             con_out("Possible optimizations:\n");
376                             for (itr = 0; itr < COUNT_OPTIMIZATIONS; ++itr) {
377                                 util_strtononcmd(opts_opt_list[itr].name, buffer, sizeof(buffer));
378                                 con_out(" -O%s\n", buffer);
379                             }
380                             exit(0);
381                         }
382                         else if (!strcmp(argarg, "ALL"))
383                             set_optimizations(opts_O = 9999);
384                         else if (!strncmp(argarg, "NO_", 3)) {
385                             if (!options_setoptim(argarg+3, false)) {
386                                 con_out("unknown optimization: %s\n", argarg+3);
387                                 return false;
388                             }
389                         }
390                         else {
391                             if (!options_setoptim(argarg, true)) {
392                                 con_out("unknown optimization: %s\n", argarg);
393                                 return false;
394                             }
395                         }
396                     }
397                     break;
398
399                 case 'o':
400                     if (!options_witharg(&argc, &argv, &argarg)) {
401                         con_out("option -o requires an argument: the output file name\n");
402                         return false;
403                     }
404                     opts_output = argarg;
405                     opts_output_wasset = true;
406                     break;
407
408                 case 'a':
409                 case 's':
410                     item.type = argv[0][1] == 'a' ? TYPE_ASM : TYPE_SRC;
411                     if (!options_witharg(&argc, &argv, &argarg)) {
412                         con_out("option -a requires a filename %s\n",
413                                 (argv[0][1] == 'a' ? "containing QC-asm" : "containing a progs.src formatted list"));
414                         return false;
415                     }
416                     item.filename = argarg;
417                     vec_push(items, item);
418                     break;
419
420                 case '-':
421                     if (!argv[0][2]) {
422                         /* anything following -- is considered a non-option argument */
423                         argend = true;
424                         break;
425                     }
426             /* All long options without arguments */
427                     else if (!strcmp(argv[0]+2, "help")) {
428                         usage();
429                         exit(0);
430                     }
431                     else {
432             /* All long options with arguments */
433                         if (options_long_witharg("output", &argc, &argv, &argarg)) {
434                             opts_output = argarg;
435                             opts_output_wasset = true;
436                         } else {
437                             con_out("Unknown parameter: %s\n", argv[0]);
438                             return false;
439                         }
440                     }
441                     break;
442
443                 default:
444                     con_out("Unknown parameter: %s\n", argv[0]);
445                     return false;
446             }
447         }
448         else
449         {
450             /* it's a QC filename */
451             item.filename = argv[0];
452             item.type     = TYPE_QC;
453             vec_push(items, item);
454         }
455     }
456     return true;
457 }
458
459 /* returns the line number, or -1 on error */
460 static bool progs_nextline(char **out, size_t *alen,FILE *src) {
461     int    len;
462     char  *line;
463     char  *start;
464     char  *end;
465
466     line = *out;
467     len = util_getline(&line, alen, src);
468     if (len == -1)
469         return false;
470
471     /* start at first non-blank */
472     for (start = line; isspace(*start); ++start) {}
473     /* end at the first non-blank */
474     for (end = start;  *end && !isspace(*end);  ++end)   {}
475
476     *out = line;
477     /* move the actual filename to the beginning */
478     while (start != end) {
479         *line++ = *start++;
480     }
481     *line = 0;
482     return true;
483 }
484
485 int main(int argc, char **argv) {
486     size_t itr;
487     int retval = 0;
488     bool opts_output_free = false;
489     bool progs_src = false;
490     FILE *outfile = NULL;
491
492     app_name = argv[0];
493     con_init();
494
495     /* default options / warn flags */
496     options_set(opts_warn, WARN_UNKNOWN_CONTROL_SEQUENCE, true);
497     options_set(opts_warn, WARN_EXTENSIONS, true);
498     options_set(opts_warn, WARN_FIELD_REDECLARED, true);
499     options_set(opts_warn, WARN_TOO_FEW_PARAMETERS, true);
500     options_set(opts_warn, WARN_MISSING_RETURN_VALUES, true);
501     options_set(opts_warn, WARN_USED_UNINITIALIZED, true);
502     options_set(opts_warn, WARN_LOCAL_CONSTANTS, true);
503     options_set(opts_warn, WARN_VOID_VARIABLES, true);
504     options_set(opts_warn, WARN_IMPLICIT_FUNCTION_POINTER, true);
505     options_set(opts_warn, WARN_VARIADIC_FUNCTION, true);
506     options_set(opts_warn, WARN_FRAME_MACROS, true);
507     options_set(opts_warn, WARN_UNUSED_VARIABLE, true);
508     options_set(opts_warn, WARN_EFFECTLESS_STATEMENT, true);
509     options_set(opts_warn, WARN_END_SYS_FIELDS, true);
510     options_set(opts_warn, WARN_ASSIGN_FUNCTION_TYPES, true);
511     options_set(opts_warn, WARN_PREPROCESSOR, true);
512     options_set(opts_warn, WARN_MULTIFILE_IF, true);
513     options_set(opts_warn, WARN_DOUBLE_DECLARATION, true);
514     options_set(opts_warn, WARN_CONST_VAR, true);
515     options_set(opts_warn, WARN_MULTIBYTE_CHARACTER, true);
516
517     options_set(opts_flags, ADJUST_VECTOR_FIELDS, true);
518     options_set(opts_flags, FTEPP, false);
519
520     if (!options_parse(argc, argv)) {
521         return usage();
522     }
523
524     /* the standard decides which set of operators to use */
525     if (opts_standard == COMPILER_GMQCC) {
526         operators = c_operators;
527         operator_count = c_operator_count;
528     } else if (opts_standard == COMPILER_FTEQCC) {
529         operators = fte_operators;
530         operator_count = fte_operator_count;
531     } else {
532         operators = qcc_operators;
533         operator_count = qcc_operator_count;
534     }
535
536     if (opts_dump) {
537         for (itr = 0; itr < COUNT_FLAGS; ++itr) {
538             con_out("Flag %s = %i\n", opts_flag_list[itr].name, OPTS_FLAG(itr));
539         }
540         for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
541             con_out("Warning %s = %i\n", opts_warn_list[itr].name, OPTS_WARN(itr));
542         }
543         con_out("output = %s\n", opts_output);
544         con_out("optimization level = %i\n", (int)opts_O);
545         con_out("standard = %i\n", opts_standard);
546     }
547
548     if (opts_pp_only) {
549         if (opts_output_wasset) {
550             outfile = util_fopen(opts_output, "wb");
551             if (!outfile) {
552                 con_err("failed to open `%s` for writing\n", opts_output);
553                 retval = 1;
554                 goto cleanup;
555             }
556         }
557         else
558             outfile = stdout;
559     }
560
561     if (!opts_pp_only) {
562         if (!parser_init()) {
563             con_err("failed to initialize parser\n");
564             retval = 1;
565             goto cleanup;
566         }
567     }
568     if (opts_pp_only || OPTS_FLAG(FTEPP)) {
569         if (!ftepp_init()) {
570             con_err("failed to initialize parser\n");
571             retval = 1;
572             goto cleanup;
573         }
574     }
575
576     util_debug("COM", "starting ...\n");
577
578     if (!vec_size(items)) {
579         FILE *src;
580         char *line;
581         size_t linelen = 0;
582
583         progs_src = true;
584
585         src = util_fopen("progs.src", "rb");
586         if (!src) {
587             con_err("failed to open `progs.src` for reading\n");
588             retval = 1;
589             goto cleanup;
590         }
591
592         line = NULL;
593         if (!progs_nextline(&line, &linelen, src) || !line[0]) {
594             con_err("illformatted progs.src file: expected output filename in first line\n");
595             retval = 1;
596             goto srcdone;
597         }
598
599         if (!opts_output_wasset) {
600             opts_output = util_strdup(line);
601             opts_output_free = true;
602         }
603
604         while (progs_nextline(&line, &linelen, src)) {
605             argitem item;
606             if (!line[0] || (line[0] == '/' && line[1] == '/'))
607                 continue;
608             item.filename = util_strdup(line);
609             item.type     = TYPE_QC;
610             vec_push(items, item);
611         }
612
613 srcdone:
614         fclose(src);
615         mem_d(line);
616     }
617
618     if (retval)
619         goto cleanup;
620
621     if (vec_size(items)) {
622         if (!opts_pp_only) {
623             con_out("Mode: %s\n", (progs_src ? "progs.src" : "manual"));
624             con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items));
625         }
626         for (itr = 0; itr < vec_size(items); ++itr) {
627             if (!opts_pp_only) {
628                 con_out("  item: %s (%s)\n",
629                        items[itr].filename,
630                        ( (items[itr].type == TYPE_QC ? "qc" :
631                          (items[itr].type == TYPE_ASM ? "asm" :
632                          (items[itr].type == TYPE_SRC ? "progs.src" :
633                          ("unknown"))))));
634             }
635
636             if (opts_pp_only) {
637                 const char *out;
638                 if (!ftepp_preprocess_file(items[itr].filename)) {
639                     retval = 1;
640                     goto cleanup;
641                 }
642                 out = ftepp_get();
643                 if (out)
644                     fprintf(outfile, "%s", out);
645                 ftepp_flush();
646             }
647             else {
648                 if (OPTS_FLAG(FTEPP)) {
649                     const char *data;
650                     if (!ftepp_preprocess_file(items[itr].filename)) {
651                         retval = 1;
652                         goto cleanup;
653                     }
654                     data = ftepp_get();
655                     if (!parser_compile_string_len(items[itr].filename, data, vec_size(data)-1)) {
656                         retval = 1;
657                         goto cleanup;
658                     }
659                     ftepp_flush();
660                 }
661                 else {
662                     if (!parser_compile_file(items[itr].filename)) {
663                         retval = 1;
664                         goto cleanup;
665                     }
666                 }
667             }
668
669             if (progs_src) {
670                 mem_d(items[itr].filename);
671                 items[itr].filename = NULL;
672             }
673         }
674
675         ftepp_finish();
676         if (!opts_pp_only) {
677             if (!parser_finish(opts_output)) {
678                 retval = 1;
679                 goto cleanup;
680             }
681         }
682     }
683
684     /* stuff */
685
686 cleanup:
687     util_debug("COM", "cleaning ...\n");
688     ftepp_finish();
689     con_close();
690     vec_free(items);
691
692     if (!opts_pp_only)
693         parser_cleanup();
694     if (opts_output_free)
695         mem_d((char*)opts_output);
696
697     lex_cleanup();
698     util_meminfo();
699     return retval;
700 }