moving -Olocal-temps to -O4 until the issues are solved
[xonotic/gmqcc.git] / main.c
1 /*
2  * Copyright (C) 2012, 2013
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 #include <time.h>
27
28 /* TODO: cleanup this whole file .. it's a fuckign mess */
29
30 /* set by the standard */
31 const oper_info *operators      = NULL;
32 size_t           operator_count = 0;
33 static bool      opts_output_wasset = false;
34
35 typedef struct { char *filename; int   type;  } argitem;
36 typedef struct { char *name;     char *value; } ppitem;
37 static argitem *items = NULL;
38 static ppitem  *ppems = NULL;
39
40 #define TYPE_QC  0
41 #define TYPE_ASM 1
42 #define TYPE_SRC 2
43
44 static const char *app_name;
45
46 static void version() {
47     con_out("GMQCC %d.%d.%d Built %s %s\n",
48         GMQCC_VERSION_MAJOR,
49         GMQCC_VERSION_MINOR,
50         GMQCC_VERSION_PATCH,
51         __DATE__,
52         __TIME__
53     );
54 }
55
56 static int usage() {
57     con_out("usage: %s [options] [files...]", app_name);
58     con_out("options:\n"
59             "  -h, --help             show this help message\n"
60             "  -debug                 turns on compiler debug messages\n"
61             "  -memchk                turns on compiler memory leak check\n");
62     con_out("  -o, --output=file      output file, defaults to progs.dat\n"
63             "  -s filename            add a progs.src file to be used\n");
64     con_out("  -E                     stop after preprocessing\n");
65     con_out("  -q, --quiet            be less verbose\n");
66     con_out("  -config file           use the specified ini file\n");
67     con_out("  -std=standard          select one of the following standards\n"
68             "       -std=qcc          original QuakeC\n"
69             "       -std=fteqcc       fteqcc QuakeC\n"
70             "       -std=gmqcc        this compiler (default)\n");
71     con_out("  -f<flag>               enable a flag\n"
72             "  -fno-<flag>            disable a flag\n"
73             "  -fhelp                 list possible flags\n");
74     con_out("  -W<warning>            enable a warning\n"
75             "  -Wno-<warning>         disable a warning\n"
76             "  -Wall                  enable all warnings\n");
77     con_out("  -Werror                treat warnings as errors\n"
78             "  -Werror-<warning>      treat a warning as error\n"
79             "  -Wno-error-<warning>   opposite of the above\n");
80     con_out("  -Whelp                 list possible warnings\n");
81     con_out("  -O<number>             optimization level\n"
82             "  -O<name>               enable specific optimization\n"
83             "  -Ono-<name>            disable specific optimization\n"
84             "  -Ohelp                 list optimizations\n");
85     con_out("  -force-crc=num         force a specific checksum into the header\n");
86     return -1;
87 }
88
89 /* command line parsing */
90 static bool options_witharg(int *argc_, char ***argv_, char **out) {
91     int  argc   = *argc_;
92     char **argv = *argv_;
93
94     if (argv[0][2]) {
95         *out = argv[0]+2;
96         return true;
97     }
98     /* eat up the next */
99     if (argc < 2) /* no parameter was provided */
100         return false;
101
102     *out = argv[1];
103     --*argc_;
104     ++*argv_;
105     return true;
106 }
107
108 static bool options_long_witharg_all(const char *optname, int *argc_, char ***argv_, char **out, int ds, bool split) {
109     int  argc   = *argc_;
110     char **argv = *argv_;
111
112     size_t len = strlen(optname);
113
114     if (strncmp(argv[0]+ds, optname, len))
115         return false;
116
117     /* it's --optname, check how the parameter is supplied */
118     if (argv[0][ds+len] == '=') {
119         /* using --opt=param */
120         *out = argv[0]+ds+len+1;
121         return true;
122     }
123
124     if (!split || argc < ds) /* no parameter was provided, or only single-arg form accepted */
125         return false;
126
127     /* using --opt param */
128     *out = argv[1];
129     --*argc_;
130     ++*argv_;
131     return true;
132 }
133 static bool options_long_witharg(const char *optname, int *argc_, char ***argv_, char **out) {
134     return options_long_witharg_all(optname, argc_, argv_, out, 2, true);
135 }
136 static bool options_long_gcc(const char *optname, int *argc_, char ***argv_, char **out) {
137     return options_long_witharg_all(optname, argc_, argv_, out, 1, false);
138 }
139
140 static bool options_parse(int argc, char **argv) {
141     bool argend = false;
142     size_t itr;
143     char  buffer[1024];
144     char *redirout = NULL;
145     char *redirerr = NULL;
146     char *config   = NULL;
147
148     while (!argend && argc > 1) {
149         char *argarg;
150         argitem item;
151         ppitem  macro;
152
153         ++argv;
154         --argc;
155
156         if (argv[0][0] == '-') {
157             /* All gcc-type long options */
158             if (options_long_gcc("std", &argc, &argv, &argarg)) {
159                 if (!strcmp(argarg, "gmqcc") || !strcmp(argarg, "default")) {
160
161                     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,          true);
162                     opts_set(opts.flags, CORRECT_LOGIC,                 true);
163                     opts_set(opts.flags, FALSE_EMPTY_STRINGS,           false);
164                     opts_set(opts.flags, TRUE_EMPTY_STRINGS,            true);
165                     opts_set(opts.flags, LOOP_LABELS,                   true);
166                     opts_set(opts.flags, TRANSLATABLE_STRINGS,          true);
167                     opts_set(opts.flags, INITIALIZED_NONCONSTANTS,      true);
168                     opts_set(opts.werror, WARN_INVALID_PARAMETER_COUNT, true);
169                     opts_set(opts.werror, WARN_MISSING_RETURN_VALUES,   true);
170                     opts.standard = COMPILER_GMQCC;
171
172                 } else if (!strcmp(argarg, "qcc")) {
173
174                     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,  false);
175                     opts_set(opts.flags, ASSIGN_FUNCTION_TYPES, true);
176                     opts.standard = COMPILER_QCC;
177
178                 } else if (!strcmp(argarg, "fte") || !strcmp(argarg, "fteqcc")) {
179
180                     opts_set(opts.flags, FTEPP,                    true);
181                     opts_set(opts.flags, TRANSLATABLE_STRINGS,     true);
182                     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,     false);
183                     opts_set(opts.flags, ASSIGN_FUNCTION_TYPES,    true);
184                     opts_set(opts.flags, CORRECT_TERNARY,          false);
185                     opts_set(opts.warn, WARN_TERNARY_PRECEDENCE,   true);
186                     opts.standard = COMPILER_FTEQCC;
187
188                 } else if (!strcmp(argarg, "qccx")) {
189
190                     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,  false);
191                     opts.standard = COMPILER_QCCX;
192
193                 } else {
194                     con_out("Unknown standard: %s\n", argarg);
195                     return false;
196                 }
197                 continue;
198             }
199             if (options_long_gcc("force-crc", &argc, &argv, &argarg)) {
200                 opts.forcecrc   = true;
201                 opts.forced_crc = strtol(argarg, NULL, 0);
202                 continue;
203             }
204             if (options_long_gcc("redirout", &argc, &argv, &redirout)) {
205                 con_change(redirout, redirerr);
206                 continue;
207             }
208             if (options_long_gcc("redirerr", &argc, &argv, &redirerr)) {
209                 con_change(redirout, redirerr);
210                 continue;
211             }
212             if (options_long_gcc("config", &argc, &argv, &argarg)) {
213                 config = argarg;
214                 continue;
215             }
216
217             /* show defaults (like pathscale) */
218             if (!strcmp(argv[0]+1, "show-defaults")) {
219                 for (itr = 0; itr < COUNT_FLAGS; ++itr) {
220                     if (!OPTS_FLAG(itr))
221                         continue;
222
223                     memset(buffer, 0, sizeof(buffer));
224                     util_strtononcmd(opts_flag_list[itr].name, buffer, strlen(opts_flag_list[itr].name) + 1);
225
226                     con_out("-f%s ", buffer);
227                 }
228                 for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
229                     if (!OPTS_WARN(itr))
230                         continue;
231
232                     memset(buffer, 0, sizeof(buffer));
233                     util_strtononcmd(opts_warn_list[itr].name, buffer, strlen(opts_warn_list[itr].name) + 1);
234                     con_out("-W%s ", buffer);
235                 }
236                 con_out("\n");
237                 exit(0);
238             }
239
240             if (!strcmp(argv[0]+1, "debug")) {
241                 opts.debug = true;
242                 continue;
243             }
244             if (!strcmp(argv[0]+1, "dump")) {
245                 opts.dump = true;
246                 continue;
247             }
248             if (!strcmp(argv[0]+1, "dumpfin")) {
249                 opts.dumpfin = true;
250                 continue;
251             }
252             if (!strcmp(argv[0]+1, "memchk")) {
253                 opts.memchk = true;
254                 continue;
255             }
256             if (!strcmp(argv[0]+1, "nocolor")) {
257                 con_color(0);
258                 continue;
259             }
260
261             switch (argv[0][1]) {
262                 /* -h, show usage but exit with 0 */
263                 case 'h':
264                     usage();
265                     exit(0);
266                     /* break; never reached because of exit(0) */
267
268                 case 'v':
269                     version();
270                     exit(0);
271
272                 case 'E':
273                     opts.pp_only = true;
274                     opts_set(opts.flags, FTEPP_PREDEFS, true); /* predefs on for -E */
275                     break;
276
277                 /* debug turns on -flno */
278                 case 'g':
279                     opts_setflag("LNO", true);
280                     opts.g = true;
281                     break;
282
283                 case 'q':
284                     opts.quiet = true;
285                     break;
286
287                 case 'D':
288                     if (!strlen(argv[0]+2)) {
289                         con_err("expected name after -D\n");
290                         exit(0);
291                     }
292
293                     if (!(argarg = strchr(argv[0] + 2, '='))) {
294                         macro.name  = util_strdup(argv[0]+2);
295                         macro.value = NULL;
296                     } else {
297                         *argarg='\0'; /* terminate for name */
298                         macro.name  = util_strdup(argv[0]+2);
299                         macro.value = util_strdup(argarg+1);
300                     }
301                     vec_push(ppems, macro);
302                     break;
303
304                 /* handle all -fflags */
305                 case 'f':
306                     util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
307                     if (!strcmp(argv[0]+2, "HELP") || *(argv[0]+2) == '?') {
308                         con_out("Possible flags:\n");
309                         for (itr = 0; itr < COUNT_FLAGS; ++itr) {
310                             util_strtononcmd(opts_flag_list[itr].name, buffer, sizeof(buffer));
311                             con_out(" -f%s\n", buffer);
312                         }
313                         exit(0);
314                     }
315                     else if (!strncmp(argv[0]+2, "NO_", 3)) {
316                         if (!opts_setflag(argv[0]+5, false)) {
317                             con_out("unknown flag: %s\n", argv[0]+2);
318                             return false;
319                         }
320                     }
321                     else if (!opts_setflag(argv[0]+2, true)) {
322                         con_out("unknown flag: %s\n", argv[0]+2);
323                         return false;
324                     }
325                     break;
326                 case 'W':
327                     util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
328                     if (!strcmp(argv[0]+2, "HELP") || *(argv[0]+2) == '?') {
329                         con_out("Possible warnings:\n");
330                         for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
331                             util_strtononcmd(opts_warn_list[itr].name, buffer, sizeof(buffer));
332                             con_out(" -W%s\n", buffer);
333                         }
334                         exit(0);
335                     }
336                     else if (!strcmp(argv[0]+2, "NO_ERROR") ||
337                              !strcmp(argv[0]+2, "NO_ERROR_ALL"))
338                     {
339                         for (itr = 0; itr < sizeof(opts.werror)/sizeof(opts.werror[0]); ++itr)
340                             opts.werror[itr] = 0;
341                         break;
342                     }
343                     else if (!strcmp(argv[0]+2, "ERROR") ||
344                              !strcmp(argv[0]+2, "ERROR_ALL"))
345                     {
346                         for (itr = 0; itr < sizeof(opts.werror)/sizeof(opts.werror[0]); ++itr)
347                             opts.werror[itr] = 0xFFFFFFFFL;
348                         break;
349                     }
350                     else if (!strcmp(argv[0]+2, "NONE")) {
351                         for (itr = 0; itr < sizeof(opts.warn)/sizeof(opts.warn[0]); ++itr)
352                             opts.warn[itr] = 0;
353                         break;
354                     }
355                     else if (!strcmp(argv[0]+2, "ALL")) {
356                         for (itr = 0; itr < sizeof(opts.warn)/sizeof(opts.warn[0]); ++itr)
357                             opts.warn[itr] = 0xFFFFFFFFL;
358                         break;
359                     }
360                     else if (!strncmp(argv[0]+2, "ERROR_", 6)) {
361                         if (!opts_setwerror(argv[0]+8, true)) {
362                             con_out("unknown warning: %s\n", argv[0]+2);
363                             return false;
364                         }
365                     }
366                     else if (!strncmp(argv[0]+2, "NO_ERROR_", 9)) {
367                         if (!opts_setwerror(argv[0]+11, false)) {
368                             con_out("unknown warning: %s\n", argv[0]+2);
369                             return false;
370                         }
371                     }
372                     else if (!strncmp(argv[0]+2, "NO_", 3)) {
373                         if (!opts_setwarn(argv[0]+5, false)) {
374                             con_out("unknown warning: %s\n", argv[0]+2);
375                             return false;
376                         }
377                     }
378                     else if (!opts_setwarn(argv[0]+2, true)) {
379                         con_out("unknown warning: %s\n", argv[0]+2);
380                         return false;
381                     }
382                     break;
383
384                 case 'O':
385                     if (!options_witharg(&argc, &argv, &argarg)) {
386                         con_out("option -O requires a numerical argument, or optimization name with an optional 'no-' prefix\n");
387                         return false;
388                     }
389                     if (isdigit(argarg[0])) {
390                         opts.O = atoi(argarg);
391                         opts_setoptimlevel(opts.O);
392                     } else {
393                         util_strtocmd(argarg, argarg, strlen(argarg)+1);
394                         if (!strcmp(argarg, "HELP")) {
395                             con_out("Possible optimizations:\n");
396                             for (itr = 0; itr < COUNT_OPTIMIZATIONS; ++itr) {
397                                 util_strtononcmd(opts_opt_list[itr].name, buffer, sizeof(buffer));
398                                 con_out(" -O%-20s (-O%u)\n", buffer, opts_opt_oflag[itr]);
399                             }
400                             exit(0);
401                         }
402                         else if (!strcmp(argarg, "ALL"))
403                             opts_setoptimlevel(opts.O = 9999);
404                         else if (!strncmp(argarg, "NO_", 3)) {
405                             if (!opts_setoptim(argarg+3, false)) {
406                                 con_out("unknown optimization: %s\n", argarg+3);
407                                 return false;
408                             }
409                         }
410                         else {
411                             if (!opts_setoptim(argarg, true)) {
412                                 con_out("unknown optimization: %s\n", argarg);
413                                 return false;
414                             }
415                         }
416                     }
417                     break;
418
419                 case 'o':
420                     if (!options_witharg(&argc, &argv, &argarg)) {
421                         con_out("option -o requires an argument: the output file name\n");
422                         return false;
423                     }
424                     opts.output = argarg;
425                     opts_output_wasset = true;
426                     break;
427
428                 case 'a':
429                 case 's':
430                     item.type = argv[0][1] == 'a' ? TYPE_ASM : TYPE_SRC;
431                     if (!options_witharg(&argc, &argv, &argarg)) {
432                         con_out("option -a requires a filename %s\n",
433                                 (argv[0][1] == 'a' ? "containing QC-asm" : "containing a progs.src formatted list"));
434                         return false;
435                     }
436                     item.filename = argarg;
437                     vec_push(items, item);
438                     break;
439
440                 case '-':
441                     if (!argv[0][2]) {
442                         /* anything following -- is considered a non-option argument */
443                         argend = true;
444                         break;
445                     }
446             /* All long options without arguments */
447                     else if (!strcmp(argv[0]+2, "help")) {
448                         usage();
449                         exit(0);
450                     }
451                     else if (!strcmp(argv[0]+2, "version")) {
452                         version();
453                         exit(0);
454                     }
455                     else if (!strcmp(argv[0]+2, "quiet")) {
456                         opts.quiet = true;
457                         break;
458                     }
459                     else {
460             /* All long options with arguments */
461                         if (options_long_witharg("output", &argc, &argv, &argarg)) {
462                             opts.output = argarg;
463                             opts_output_wasset = true;
464                         } else {
465                             con_out("Unknown parameter: %s\n", argv[0]);
466                             return false;
467                         }
468                     }
469                     break;
470
471                 default:
472                     con_out("Unknown parameter: %s\n", argv[0]);
473                     return false;
474             }
475         }
476         else
477         {
478             /* it's a QC filename */
479             item.filename = argv[0];
480             item.type     = TYPE_QC;
481             vec_push(items, item);
482         }
483     }
484     opts_ini_init(config);
485     return true;
486 }
487
488 /* returns the line number, or -1 on error */
489 static bool progs_nextline(char **out, size_t *alen,FILE *src) {
490     int    len;
491     char  *line;
492     char  *start;
493     char  *end;
494
495     line = *out;
496     len  = file_getline(&line, alen, src);
497     if (len == -1)
498         return false;
499
500     /* start at first non-blank */
501     for (start = line; isspace(*start); ++start) {}
502     /* end at the first non-blank */
503     for (end = start;  *end && !isspace(*end);  ++end)   {}
504
505     *out = line;
506     /* move the actual filename to the beginning */
507     while (start != end) {
508         *line++ = *start++;
509     }
510     *line = 0;
511     return true;
512 }
513
514 int main(int argc, char **argv) {
515     size_t itr;
516     int    retval           = 0;
517     bool   opts_output_free = false;
518     bool   operators_free   = false;
519     bool   progs_src        = false;
520     FILE  *outfile          = NULL;
521
522     app_name = argv[0];
523     con_init ();
524     opts_init("progs.dat", COMPILER_GMQCC, (1024 << 3));
525
526     util_seed(time(0));
527
528     if (!options_parse(argc, argv)) {
529         return usage();
530     }
531
532     if (OPTS_FLAG(TRUE_EMPTY_STRINGS) && OPTS_FLAG(FALSE_EMPTY_STRINGS)) {
533         con_err("-ftrue-empty-strings and -ffalse-empty-strings are mutually exclusive");
534         exit(EXIT_FAILURE);
535     }
536
537     /* the standard decides which set of operators to use */
538     if (opts.standard == COMPILER_GMQCC) {
539         operators      = c_operators;
540         operator_count = c_operator_count;
541     } else if (opts.standard == COMPILER_FTEQCC) {
542         operators      = fte_operators;
543         operator_count = fte_operator_count;
544     } else {
545         operators      = qcc_operators;
546         operator_count = qcc_operator_count;
547     }
548
549     if (operators == fte_operators) {
550         /* fix ternary? */
551         if (OPTS_FLAG(CORRECT_TERNARY)) {
552             oper_info *newops;
553             if (operators[operator_count-2].id != opid1(',') ||
554                 operators[operator_count-1].id != opid2(':','?'))
555             {
556                 con_err("internal error: operator precedence table wasn't updated correctly!\n");
557                 exit(EXIT_FAILURE);
558             }
559             operators_free = true;
560             newops = (oper_info*)mem_a(sizeof(operators[0]) * operator_count);
561             memcpy(newops, operators, sizeof(operators[0]) * operator_count);
562             memcpy(&newops[operator_count-2], &operators[operator_count-1], sizeof(newops[0]));
563             memcpy(&newops[operator_count-1], &operators[operator_count-2], sizeof(newops[0]));
564             newops[operator_count-2].prec = newops[operator_count-1].prec+1;
565             operators = newops;
566         }
567     }
568
569     if (opts.dump) {
570         for (itr = 0; itr < COUNT_FLAGS; ++itr)
571             con_out("Flag %s = %i\n",    opts_flag_list[itr].name, OPTS_FLAG(itr));
572         for (itr = 0; itr < COUNT_WARNINGS; ++itr)
573             con_out("Warning %s = %i\n", opts_warn_list[itr].name, OPTS_WARN(itr));
574
575         con_out("output             = %s\n", opts.output);
576         con_out("optimization level = %d\n", opts.O);
577         con_out("standard           = %i\n", opts.standard);
578     }
579
580     if (opts.pp_only) {
581         if (opts_output_wasset) {
582             outfile = file_open(opts.output, "wb");
583             if (!outfile) {
584                 con_err("failed to open `%s` for writing\n", opts.output);
585                 retval = 1;
586                 goto cleanup;
587             }
588         }
589         else {
590             outfile = con_default_out();
591         }
592     }
593
594     if (!opts.pp_only) {
595         if (!parser_init()) {
596             con_err("failed to initialize parser\n");
597             retval = 1;
598             goto cleanup;
599         }
600     }
601
602     if (opts.pp_only || OPTS_FLAG(FTEPP)) {
603         if (!ftepp_init()) {
604             con_err("failed to initialize parser\n");
605             retval = 1;
606             goto cleanup;
607         }
608     }
609
610     if (OPTS_FLAG(TRUE_EMPTY_STRINGS))
611         type_not_instr[TYPE_STRING] = INSTR_NOT_F;
612
613     util_debug("COM", "starting ...\n");
614
615     /* add macros */
616     if (opts.pp_only || OPTS_FLAG(FTEPP)) {
617         for (itr = 0; itr < vec_size(ppems); itr++) {
618             ftepp_add_macro(ppems[itr].name, ppems[itr].value);
619             mem_d(ppems[itr].name);
620
621             /* can be null */
622             if (ppems[itr].value)
623                 mem_d(ppems[itr].value);
624         }
625     }
626
627     if (!vec_size(items)) {
628         FILE *src;
629         char *line;
630         size_t linelen = 0;
631
632         progs_src = true;
633
634         src = file_open("progs.src", "rb");
635         if (!src) {
636             con_err("failed to open `progs.src` for reading\n");
637             retval = 1;
638             goto cleanup;
639         }
640
641         line = NULL;
642         if (!progs_nextline(&line, &linelen, src) || !line[0]) {
643             con_err("illformatted progs.src file: expected output filename in first line\n");
644             retval = 1;
645             goto srcdone;
646         }
647
648         if (!opts_output_wasset) {
649             opts.output = util_strdup(line);
650             opts_output_free = true;
651         }
652
653         while (progs_nextline(&line, &linelen, src)) {
654             argitem item;
655             if (!line[0] || (line[0] == '/' && line[1] == '/'))
656                 continue;
657             item.filename = util_strdup(line);
658             item.type     = TYPE_QC;
659             vec_push(items, item);
660         }
661
662 srcdone:
663         file_close(src);
664         mem_d(line);
665     }
666
667     if (retval)
668         goto cleanup;
669
670     if (vec_size(items)) {
671         if (!opts.quiet && !opts.pp_only) {
672             con_out("Mode: %s\n", (progs_src ? "progs.src" : "manual"));
673             con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items));
674         }
675         for (itr = 0; itr < vec_size(items); ++itr) {
676             if (!opts.quiet && !opts.pp_only) {
677                 con_out("  item: %s (%s)\n",
678                        items[itr].filename,
679                        ( (items[itr].type == TYPE_QC ? "qc" :
680                          (items[itr].type == TYPE_ASM ? "asm" :
681                          (items[itr].type == TYPE_SRC ? "progs.src" :
682                          ("unknown"))))));
683             }
684
685             if (opts.pp_only) {
686                 const char *out;
687                 if (!ftepp_preprocess_file(items[itr].filename)) {
688                     retval = 1;
689                     goto cleanup;
690                 }
691                 out = ftepp_get();
692                 if (out)
693                     file_printf(outfile, "%s", out);
694                 ftepp_flush();
695             }
696             else {
697                 if (OPTS_FLAG(FTEPP)) {
698                     const char *data;
699                     if (!ftepp_preprocess_file(items[itr].filename)) {
700                         retval = 1;
701                         goto cleanup;
702                     }
703                     data = ftepp_get();
704                     if (vec_size(data)) {
705                         if (!parser_compile_string(items[itr].filename, data, vec_size(data))) {
706                             retval = 1;
707                             goto cleanup;
708                         }
709                     }
710                     ftepp_flush();
711                 }
712                 else {
713                     if (!parser_compile_file(items[itr].filename)) {
714                         retval = 1;
715                         goto cleanup;
716                     }
717                 }
718             }
719
720             if (progs_src) {
721                 mem_d(items[itr].filename);
722                 items[itr].filename = NULL;
723             }
724         }
725
726         ftepp_finish();
727         if (!opts.pp_only) {
728             if (!parser_finish(opts.output)) {
729                 retval = 1;
730                 goto cleanup;
731             }
732         }
733     }
734
735     /* stuff */
736     if (!opts.quiet && !opts.pp_only) {
737         for (itr = 0; itr < COUNT_OPTIMIZATIONS; ++itr) {
738             if (opts_optimizationcount[itr]) {
739                 con_out("%s: %u\n", opts_opt_list[itr].name, (unsigned int)opts_optimizationcount[itr]);
740             }
741         }
742     }
743
744 cleanup:
745     util_debug("COM", "cleaning ...\n");
746     ftepp_finish();
747     con_close();
748     vec_free(items);
749     vec_free(ppems);
750
751     if (!opts.pp_only)
752         parser_cleanup();
753     if (opts_output_free)
754         mem_d((char*)opts.output);
755     if (operators_free)
756         mem_d((void*)operators);
757
758     lex_cleanup();
759     util_meminfo();
760     return retval;
761 }